mini-static 0.17.0

A secure, async static file server with streaming, traversal protection, and connection limits.
Documentation
# mini-static API Reference

## Server Initialization

```rust
use mini_static::Server;
use std::path::Path;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let server = Server::new(Path::new("./public"))?;
    let (_port, handle) = server.run_ephemeral().await?;
    
    // Graceful shutdown
    handle.shutdown().await;
    Ok(())
}
```

## Configuration

### max_connections
Control concurrent connection limits:

```rust
let server = Server::new(Path::new("./public"))?
    .with_max_connections(512);
```

Default: 1024 concurrent connections

### minify

Enable CSS/JS minification:

```rust
let server = Server::new(Path::new("./public"))?
    .with_minify();
```

Default: disabled. Minified output is cached in-memory, keyed by file path and mtime.

### css_bundling and bundle_root

Enable recursive `@import` bundling and register additional source directories:

```rust
let server = Server::new(Path::new("./public"))?
    .with_minify()
    .with_css_bundling()
    .with_bundle_root(Path::new("./styles/shared"))?
    .with_bundle_root(Path::new("./styles/tokens"))?;
```

- `with_css_bundling()` — recursively inlines `@import` statements in CSS files
  (requires `with_minify()` first).
- `with_bundle_root(path)` — registers an external source directory for import resolution.
  Can be called multiple times to add multiple external roots. Files under a bundle root
  can be imported but are never directly HTTP-servable.

Default: bundling disabled.

### run() vs run_ephemeral()

- `run_ephemeral()` — binds to `127.0.0.1` with 30-second header timeout
- `run(Duration)` — binds to `127.0.0.1` with custom header timeout

Both return `(port, ServerHandle)` for graceful shutdown.

## Request Handling

Supports GET and HEAD methods on any path within the root directory.

### Responses

- **200 OK** — file content with ETag and Content-Length
- **304 Not Modified** — if If-None-Match or If-Modified-Since match
- **404 Not Found** — file not found or traversal attempt
- **405 Method Not Allowed** — methods other than GET/HEAD

### Conditional Requests

Clients can optimize bandwidth with conditional headers:

```
GET /file.js HTTP/1.1
If-None-Match: "1024-1721936400"
```

Returns 304 if the ETag matches, allowing the client to use a cached copy.

## Security

- Path traversal protection via segment-based checking
- All 404 responses identical (no filesystem oracle)
- Non-ASCII filename support with full traversal guards
- Header-read timeout prevents slowloris attacks
- Runs as unprivileged user in Docker

## Performance

- Real streaming: 64 KB chunks, bounded memory per request
- No per-chunk zero-fill overhead
- Blocking syscalls (`canonicalize`, file I/O) on dedicated thread pool
- Graceful backoff on transient accept() errors