mini-static 0.31.3

A secure, async static file server with streaming, traversal protection, and connection limits.
Documentation
use super::*;

use std::fs as std_fs;
use tempfile::TempDir;

fn metadata_for(dir: &TempDir, name: &str, contents: &[u8]) -> std_fs::Metadata {
    let path = dir.path().join(name);
    std_fs::write(&path, contents).unwrap();
    std_fs::metadata(&path).unwrap()
}

/// The validator must carry sub-second precision, since that is the entire justification
/// for serving an ETag instead of `Last-Modified`.
#[test]
fn an_etag_carries_a_sub_second_component() {
    let dir = TempDir::new().unwrap();
    let etag = generate_etag(&metadata_for(&dir, "a.txt", b"contents"));

    let (size, time) = etag
        .trim_matches('"')
        .split_once('-')
        .expect("format is \"<size>-<secs>.<nanos>\"");
    assert_eq!(size, "8");
    assert!(
        time.contains('.'),
        "expected a <secs>.<nanos> time component, got: {time}"
    );
}

/// The bug this format exists to prevent: two writes of equal length, close enough
/// together to share a whole second, previously produced identical ETags — so a client
/// revalidating against the first was told `304 Not Modified` while holding stale bytes.
#[test]
fn two_same_length_writes_in_quick_succession_differ() {
    let dir = TempDir::new().unwrap();

    let first = generate_etag(&metadata_for(&dir, "asset.js", b"aaaaa"));
    let second = generate_etag(&metadata_for(&dir, "asset.js", b"bbbbb"));

    assert_ne!(
        first, second,
        "an equal-length rewrite must change the validator"
    );
}

/// Length alone still distinguishes representations, including on a filesystem whose
/// timestamps are coarse enough to collide.
#[test]
fn different_lengths_produce_different_etags() {
    let dir = TempDir::new().unwrap();

    let short = generate_etag(&metadata_for(&dir, "a.txt", b"aa"));
    let long = generate_etag(&metadata_for(&dir, "b.txt", b"aaaaaaaa"));

    assert_ne!(short, long);
}

/// Nothing about the ETag may vary between two reads of an unchanged file, or every
/// revalidation would miss and the validator would be worthless.
#[test]
fn an_unchanged_file_keeps_its_etag() {
    let dir = TempDir::new().unwrap();
    let path = dir.path().join("stable.txt");
    std_fs::write(&path, b"unchanged").unwrap();

    let first = generate_etag(&std_fs::metadata(&path).unwrap());
    let second = generate_etag(&std_fs::metadata(&path).unwrap());

    assert_eq!(first, second);
}