mod common;
use std::fs;
use hyper::{Method, Request};
use mini_static::Server;
use tempfile::TempDir;
fn root() -> TempDir {
let root = TempDir::new().unwrap();
fs::create_dir(root.path().join("admin")).unwrap();
fs::write(root.path().join("admin/config"), b"SECRET").unwrap();
fs::write(root.path().join("page.html"), b"<html>page</html>").unwrap();
root
}
fn request(path: &str) -> Request<()> {
Request::builder().method(Method::GET).uri(path).body(()).unwrap()
}
#[tokio::test]
async fn segments_from_a_router_resolve_the_file() {
let root = root();
let server = Server::new(root.path()).unwrap();
let segments = vec!["admin".to_string(), "config".to_string()];
let response = server.respond(&request("/admin/config"), &segments).await;
assert_eq!(response.status().as_u16(), 200);
assert_eq!(&common::body_bytes(response).await[..], b"SECRET");
}
#[tokio::test]
async fn a_segment_containing_a_separator_is_refused() {
let root = root();
let server = Server::new(root.path()).unwrap();
let segments = vec!["admin/config".to_string()];
let response = server.respond(&request("/admin%2Fconfig"), &segments).await;
assert_eq!(
response.status().as_u16(),
404,
"a segment holding a literal separator reached a nested file"
);
}
#[tokio::test]
async fn a_dot_dot_segment_is_refused() {
let root = root();
let server = Server::new(root.path()).unwrap().with_hidden_files();
let segments = vec!["admin".to_string(), "..".to_string(), "page.html".to_string()];
let response = server.respond(&request("/admin/../page.html"), &segments).await;
assert_eq!(response.status().as_u16(), 404, "a `..` segment was followed");
}
#[tokio::test]
async fn a_segment_containing_a_backslash_is_refused() {
let root = root();
fs::write(root.path().join(r"admin\config"), b"BACKSLASH-NAMED").unwrap();
let server = Server::new(root.path()).unwrap();
let segments = vec![r"admin\config".to_string()];
let response = server.respond(&request("/admin%5Cconfig"), &segments).await;
assert_eq!(response.status().as_u16(), 404);
}
#[tokio::test]
async fn respond_and_handle_request_agree() {
let root = root();
let server = Server::new(root.path()).unwrap();
let via_handle = common::get(&server, "/page.html").await;
let segments = vec!["page.html".to_string()];
let via_respond = server.respond(&request("/page.html"), &segments).await;
assert_eq!(via_handle.status(), via_respond.status());
assert_eq!(
common::body_bytes(via_handle).await,
common::body_bytes(via_respond).await
);
}
#[tokio::test]
async fn respond_refuses_a_method_it_does_not_serve() {
let root = root();
let server = Server::new(root.path()).unwrap();
let req = Request::builder()
.method(Method::DELETE)
.uri("/page.html")
.body(())
.unwrap();
let response = server.respond(&req, &["page.html".to_string()]).await;
assert_eq!(response.status().as_u16(), 405);
}