mirror of
https://github.com/static-web-server/static-web-server.git
synced 2026-01-25 05:06:33 +00:00
* test: hidden base path not ignored * fix: hidden base path not ignored * feat: trace when ignoring a hidden file
1839 lines
70 KiB
Rust
1839 lines
70 KiB
Rust
#![forbid(unsafe_code)]
|
|
#![deny(warnings)]
|
|
#![deny(rust_2018_idioms)]
|
|
#![deny(dead_code)]
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use bytes::Bytes;
|
|
use headers::HeaderMap;
|
|
use http::{Method, StatusCode};
|
|
use static_web_server::http_ext::MethodExt;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
#[cfg(any(
|
|
feature = "compression",
|
|
feature = "compression-deflate",
|
|
feature = "compression-gzip",
|
|
feature = "compression-brotli",
|
|
feature = "compression-zstd"
|
|
))]
|
|
use static_web_server::compression;
|
|
|
|
#[cfg(feature = "directory-listing")]
|
|
use static_web_server::directory_listing::DirListFmt;
|
|
use static_web_server::static_files::{self, HandleOpts};
|
|
|
|
fn root_dir() -> PathBuf {
|
|
PathBuf::from("tests/fixtures/public/")
|
|
}
|
|
|
|
const METHODS: [Method; 8] = [
|
|
Method::CONNECT,
|
|
Method::DELETE,
|
|
Method::GET,
|
|
Method::HEAD,
|
|
Method::PATCH,
|
|
Method::POST,
|
|
Method::PUT,
|
|
Method::TRACE,
|
|
];
|
|
|
|
#[tokio::test]
|
|
async fn handle_file() {
|
|
let result = static_files::handle(&HandleOpts {
|
|
method: &Method::GET,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
.expect("unexpected error response on `handle` function");
|
|
let mut res = result.resp;
|
|
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
assert_eq!(res.headers()["accept-ranges"], "bytes");
|
|
assert!(!res.headers()["last-modified"].is_empty());
|
|
|
|
let ctype = &res.headers()["content-type"];
|
|
|
|
assert!(ctype == "text/html", "content-type is not html: {ctype:?}",);
|
|
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
|
|
assert_eq!(body, buf);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_file_head() {
|
|
let result = static_files::handle(&HandleOpts {
|
|
method: &Method::HEAD,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
.expect("unexpected error response on `handle` function");
|
|
let mut res = result.resp;
|
|
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
assert_eq!(res.headers()["accept-ranges"], "bytes");
|
|
assert!(!res.headers()["last-modified"].is_empty());
|
|
|
|
let ctype = &res.headers()["content-type"];
|
|
|
|
assert!(ctype == "text/html", "content-type is not html: {ctype:?}",);
|
|
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
|
|
assert_eq!(body, buf);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_file_not_found() {
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "xyz.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
panic!("expected a status error 404 but not status 200")
|
|
}
|
|
Err(status) => {
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_trailing_slash_redirection() {
|
|
let result = static_files::handle(&HandleOpts {
|
|
method: &Method::GET,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "assets",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 0,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
.expect("unexpected error response on `handle` function");
|
|
let mut res = result.resp;
|
|
|
|
assert_eq!(res.status(), 308);
|
|
assert_eq!(res.headers()["location"], "assets/");
|
|
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
|
|
assert_eq!(body, Bytes::new());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_trailing_slash_redirection_subdir() {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &Method::GET,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "assets",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 0,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 308);
|
|
assert_eq!(res.headers()["location"], "assets/");
|
|
}
|
|
Err(status) => {
|
|
panic!("expected a status 308 but not a status {status}")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_disabled_trailing_slash_redirection_subdir() {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &Method::GET,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "assets",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 0,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: false,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
}
|
|
Err(status) => {
|
|
panic!("expected a status 200 but not a status {status}")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_append_index_on_dir() {
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
for uri in ["/assets", "/assets/"] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: uri,
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
if uri == "/assets" {
|
|
// it should redirect permanently
|
|
assert_eq!(res.status(), 308);
|
|
assert_eq!(res.headers()["location"], "/assets/");
|
|
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
|
|
assert_eq!(body, Bytes::new());
|
|
} else {
|
|
// otherwise it should response with ok
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
}
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_file_encoded() {
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "/assets/index%2ehtml",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_bad_encoded_path() {
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "/%2E%2e.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
Err(status) => {
|
|
assert_eq!(status, 404);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_not_modified() {
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
let res1 = match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
res
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
};
|
|
|
|
// if-modified-since
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert(
|
|
"if-modified-since",
|
|
res1.headers()["last-modified"].to_owned(),
|
|
);
|
|
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 304);
|
|
assert_eq!(res.headers().get("content-length"), None);
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, "");
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 304 but not a status error")
|
|
}
|
|
}
|
|
|
|
// clearly too old
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert(
|
|
"if-modified-since",
|
|
"Mon, 18 Nov 1974 00:00:00 GMT".parse().unwrap(),
|
|
);
|
|
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, buf);
|
|
assert_eq!(res1.headers()["content-length"], buf.len().to_string());
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_precondition() {
|
|
for method in [Method::HEAD, Method::GET] {
|
|
let res1 = match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
res
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
};
|
|
|
|
// if-unmodified-since
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert(
|
|
"if-unmodified-since",
|
|
res1.headers()["last-modified"].to_owned(),
|
|
);
|
|
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
}
|
|
|
|
// clearly too old
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert(
|
|
"if-unmodified-since",
|
|
"Mon, 18 Nov 1974 00:00:00 GMT".parse().unwrap(),
|
|
);
|
|
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 412);
|
|
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
|
|
assert_eq!(body, "");
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a status 200 but not a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_file_allowed_disallowed_methods() {
|
|
for method in METHODS {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &HeaderMap::new(),
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => match method {
|
|
// The handle only accepts HEAD or GET request methods
|
|
Method::GET | Method::HEAD => {
|
|
let mut res = result.resp;
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
assert_eq!(res.headers()["accept-ranges"], "bytes");
|
|
assert!(!res.headers()["last-modified"].is_empty());
|
|
|
|
let ctype = &res.headers()["content-type"];
|
|
|
|
assert!(ctype == "text/html", "content-type is not html: {ctype:?}",);
|
|
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
|
|
assert_eq!(body, buf);
|
|
}
|
|
_ => {
|
|
panic!("unexpected response for method {}", method.as_str())
|
|
}
|
|
},
|
|
Err(status) => {
|
|
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[cfg(any(
|
|
feature = "compression",
|
|
feature = "compression-deflate",
|
|
feature = "compression-gzip",
|
|
feature = "compression-brotli",
|
|
feature = "compression-zstd"
|
|
))]
|
|
async fn handle_file_compressions() {
|
|
let encodings = [
|
|
#[cfg(any(feature = "compression", feature = "compression-gzip"))]
|
|
"gzip",
|
|
#[cfg(any(feature = "compression", feature = "compression-deflate"))]
|
|
"deflate",
|
|
#[cfg(any(feature = "compression", feature = "compression-brotli"))]
|
|
"br",
|
|
#[cfg(any(feature = "compression", feature = "compression-zstd"))]
|
|
"zstd",
|
|
"xyz",
|
|
];
|
|
let method = &Method::GET;
|
|
|
|
for enc in encodings {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert(
|
|
http::header::ACCEPT_ENCODING,
|
|
format!("identity, {enc}").parse().unwrap(),
|
|
);
|
|
|
|
match static_files::handle(&HandleOpts {
|
|
method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
let res = compression::auto(
|
|
method,
|
|
&headers,
|
|
static_web_server::settings::CompressionLevel::Default,
|
|
res,
|
|
)
|
|
.expect("unexpected bytes error during body compression");
|
|
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["accept-ranges"], "bytes");
|
|
assert!(!res.headers()["last-modified"].is_empty());
|
|
|
|
match enc {
|
|
// The handle only accepts `HEAD` or `GET` request methods
|
|
"gzip" | "deflate" | "br" | "zstd" => {
|
|
assert!(res.headers().get("content-length").is_none());
|
|
assert_eq!(res.headers()["content-encoding"], enc);
|
|
}
|
|
_ => {
|
|
// otherwise the compression doesn't happen because unsupported `accept-encoding`
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
assert!(res.headers().get("content-encoding").is_none());
|
|
}
|
|
};
|
|
|
|
let ctype = &res.headers()["content-type"];
|
|
|
|
assert!(ctype == "text/html", "content-type is not html: {ctype:?}",);
|
|
}
|
|
Err(_) => {
|
|
panic!("unexpected status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_single() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=0-0".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 206);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes 0-0/{}", buf.len())
|
|
);
|
|
assert_eq!(res.headers()["content-length"], "1");
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, &buf[..=0]);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_multiple() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=100-200".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "assets/index.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 206);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes 100-200/{}", buf.len())
|
|
);
|
|
assert_eq!(res.headers()["content-length"], "101");
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, &buf[100..=200]);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_out_of_range() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=100-100000".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "assets/index.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 206);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes 100-{}/{}", buf.len() - 1, buf.len())
|
|
);
|
|
assert!(res.headers().get("content-length").is_some());
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert!(body.len() > 400);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_if_range_too_old() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=100-200".parse().unwrap());
|
|
headers.insert("if-range", "Mon, 18 Nov 1974 00:00:00 GMT".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
assert_eq!(res.headers().get("content-range"), None);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_suffix() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=100-".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "assets/index.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 206);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes 100-{}/{}", buf.len() - 1, buf.len())
|
|
);
|
|
assert_eq!(
|
|
res.headers()["content-length"],
|
|
&buf[100..].len().to_string()
|
|
);
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, &buf[100..]);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_suffix_2() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=-100".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "assets/index.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 206);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes {}-{}/{}", buf.len() - 100, buf.len() - 1, buf.len())
|
|
);
|
|
assert_eq!(res.headers()["content-length"], "100");
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, &buf[buf.len() - 100..]);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_bad() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=100-10".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 416);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes */{}", buf.len())
|
|
);
|
|
assert_eq!(res.headers().get("content-length"), None);
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, "");
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_bad_non_numeric() {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("range", "bytes=xyx-abc".parse().unwrap());
|
|
|
|
let buf = fs::read(root_dir().join("index.htm"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 416);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes */{}", buf.len())
|
|
);
|
|
assert!(res.headers().get("content-length").is_none());
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert!(body.is_empty());
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_bad_2() {
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert(
|
|
"range",
|
|
format!("bytes=-{}", buf.len() + 1).parse().unwrap(),
|
|
);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "assets/index.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
assert!(res.headers().get("content-length").is_some());
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert!(body.len() > 500);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_bad_3() {
|
|
let mut headers = HeaderMap::new();
|
|
// Range::Unbounded for beginning and end
|
|
headers.insert("range", "bytes=".parse().unwrap());
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "index.htm",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 416);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_exclude_file_size() {
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
let mut headers = HeaderMap::new();
|
|
// range including end of file (non-inclusive result)
|
|
headers.insert("range", format!("bytes=100-{}", buf.len()).parse().unwrap());
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "assets/index.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 206);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes 100-{}/{}", buf.len() - 1, buf.len())
|
|
);
|
|
assert_eq!(
|
|
res.headers()["content-length"],
|
|
format!("{}", buf.len() - 100)
|
|
);
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, &buf[100..=buf.len() - 1]);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_byte_ranges_exclude_file_size_2() {
|
|
let buf = fs::read(root_dir().join("assets/index.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
let mut headers = HeaderMap::new();
|
|
// range with 1 byte to end yields same result as above. (inclusive result)
|
|
headers.insert(
|
|
"range",
|
|
format!("bytes=100-{}", buf.len() - 1).parse().unwrap(),
|
|
);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir(),
|
|
uri_path: "assets/index.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: false,
|
|
ignore_hidden_files: false,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 206);
|
|
assert_eq!(
|
|
res.headers()["content-range"],
|
|
format!("bytes 100-{}/{}", buf.len() - 1, buf.len())
|
|
);
|
|
assert_eq!(
|
|
res.headers()["content-length"],
|
|
format!("{}", buf.len() - 100)
|
|
);
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, &buf[100..=buf.len() - 1]);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_ignore_hidden_files() {
|
|
let root_dir = PathBuf::from("tests/fixtures/public/");
|
|
let headers = HeaderMap::new();
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: ".dotfile",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
panic!("expected a status error 404 but not status 200")
|
|
}
|
|
Err(status) => {
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn hidden_base_path_not_ignored() {
|
|
let root_dir = PathBuf::from("tests/fixtures/.hidden-root");
|
|
let headers = HeaderMap::new();
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
let result = static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: "foo.html",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: false,
|
|
index_files: &[],
|
|
})
|
|
.await
|
|
.expect("unexpected error response on `handle` function");
|
|
let mut res = result.resp;
|
|
|
|
let buf = fs::read(root_dir.join("foo.html"))
|
|
.expect("unexpected error during index.html reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], buf.len().to_string());
|
|
assert_eq!(res.headers()["accept-ranges"], "bytes");
|
|
assert!(!res.headers()["last-modified"].is_empty());
|
|
|
|
let ctype = &res.headers()["content-type"];
|
|
|
|
assert!(ctype == "text/html", "content-type is not html: {ctype:?}",);
|
|
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
|
|
assert_eq!(body, buf);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn hidden_file_in_hidden_base_path_ignored() {
|
|
let root_dir = PathBuf::from("tests/fixtures/.hidden-root");
|
|
let headers = HeaderMap::new();
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: ".hidden-file.txt",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
panic!("expected a status error 404 but not status 200")
|
|
}
|
|
Err(status) => {
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_multiple_index_files() {
|
|
let root_dir = PathBuf::from("tests/fixtures/public/");
|
|
let headers = HeaderMap::new();
|
|
|
|
let buf = fs::read(root_dir.join("index.htm"))
|
|
.expect("unexpected error during index.htm reading");
|
|
let buf = Bytes::from(buf);
|
|
|
|
for method in [Method::HEAD, Method::GET] {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: "/",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm", "index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let mut res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
assert_eq!(res.headers()["content-length"], format!("{}", buf.len()));
|
|
let body = hyper::body::to_bytes(res.body_mut())
|
|
.await
|
|
.expect("unexpected bytes error during `body` conversion");
|
|
assert_eq!(body, &buf);
|
|
}
|
|
Err(_) => {
|
|
panic!("expected a normal response rather than a status error")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_disable_symlinks() {
|
|
let root_dir = PathBuf::from("tests/fixtures/public/");
|
|
let headers = HeaderMap::new();
|
|
|
|
for method in METHODS {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: "/symlink",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: true,
|
|
index_files: &["index.htm", "index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(_) => panic!("unexpected successful response rather than an error"),
|
|
Err(err) => {
|
|
match method {
|
|
// The handle only accepts HEAD or GET request methods
|
|
Method::GET | Method::HEAD => assert_eq!(err, StatusCode::FORBIDDEN),
|
|
_ => assert_eq!(err, StatusCode::METHOD_NOT_ALLOWED),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for method in METHODS {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: "/symlink/spécial file.txt~",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm", "index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(result) => {
|
|
let res = result.resp;
|
|
assert_eq!(res.status(), 200);
|
|
}
|
|
Err(status) => match method {
|
|
Method::GET | Method::HEAD => {
|
|
panic!("unexpected error response with status {status}")
|
|
}
|
|
_ => assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_symlinks_paths() {
|
|
let root_dir_rel = PathBuf::from("tests/fixtures/public/");
|
|
let root_dir_abs = root_dir_rel.canonicalize().unwrap();
|
|
let headers = HeaderMap::new();
|
|
|
|
for root_dir in [root_dir_rel, root_dir_abs] {
|
|
for method in METHODS {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: "/readme.md",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm", "index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
panic!("unexpected successful response")
|
|
}
|
|
Err(status) => {
|
|
if method.is_allowed() {
|
|
assert_eq!(status, StatusCode::NOT_FOUND)
|
|
} else {
|
|
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn handle_symlinks_skip_broken_path() {
|
|
let root_dir_rel = PathBuf::from("tests/fixtures/symlink/");
|
|
let root_dir_abs = root_dir_rel.canonicalize().unwrap();
|
|
let headers = HeaderMap::new();
|
|
|
|
for root_dir in [root_dir_rel, root_dir_abs] {
|
|
for method in METHODS {
|
|
match static_files::handle(&HandleOpts {
|
|
method: &method,
|
|
headers: &headers,
|
|
base_path: &root_dir,
|
|
uri_path: "/unknown.md",
|
|
uri_query: None,
|
|
#[cfg(feature = "experimental")]
|
|
memory_cache: None,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing: false,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_order: 6,
|
|
#[cfg(feature = "directory-listing")]
|
|
dir_listing_format: &DirListFmt::Html,
|
|
#[cfg(feature = "directory-listing-download")]
|
|
dir_listing_download: &[],
|
|
redirect_trailing_slash: true,
|
|
compression_static: true,
|
|
ignore_hidden_files: true,
|
|
disable_symlinks: false,
|
|
index_files: &["index.htm", "index.htm"],
|
|
})
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
panic!("unexpected successful response")
|
|
}
|
|
Err(status) => {
|
|
if method.is_allowed() {
|
|
assert_eq!(status, StatusCode::NOT_FOUND)
|
|
} else {
|
|
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|