mini-static 0.17.0

A secure, async static file server with streaming, traversal protection, and connection limits.
Documentation
mod common;

use mini_static::Server;
use std::fs;
use tempfile::TempDir;

/// A traversal attempt and a genuinely missing file must be indistinguishable in the
/// response — same status and same body — or the difference itself discloses whether a
/// path exists outside the root.
#[tokio::test]
async fn traversal_and_missing_file_are_indistinguishable() {
    let root = TempDir::new().unwrap();
    fs::write(root.path().join("exists.txt"), b"content").unwrap();
    let server = Server::new(root.path()).unwrap();

    let traversal = common::get(&server, "/../../../etc/passwd").await;
    let missing = common::get(&server, "/definitely-not-a-real-file-xyz.txt").await;

    assert_eq!(traversal.status().as_u16(), 404);
    assert_eq!(missing.status().as_u16(), 404);

    let traversal_body = common::body_bytes(traversal).await;
    let missing_body = common::body_bytes(missing).await;
    assert_eq!(
        traversal_body, missing_body,
        "the two bodies must be byte-identical, or the response leaks which case occurred"
    );
}

#[tokio::test]
async fn every_response_carries_nosniff_and_success_returns_the_real_file_content() {
    let root = TempDir::new().unwrap();
    fs::write(root.path().join("test.txt"), b"content").unwrap();
    let server = Server::new(root.path()).unwrap();

    for path in ["/../etc/passwd", "/nonexistent.txt", "/test.txt"] {
        let response = common::get(&server, path).await;
        assert_eq!(
            response
                .headers()
                .get("X-Content-Type-Options")
                .map(|v| v.to_str().unwrap()),
            Some("nosniff"),
            "{path} should carry nosniff"
        );
    }

    // The success case must return the file's actual content, not a placeholder — this
    // is the check that would have caught `handle_request` answering every successful
    // resolve with a hardcoded "ok\n" body regardless of the file's real content.
    let success = common::get(&server, "/test.txt").await;
    assert_eq!(success.status().as_u16(), 200);
    let body = common::body_bytes(success).await;
    assert_eq!(
        &body[..],
        b"content",
        "success response body should be the file's real content"
    );
}