mini-static 0.6.2

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

### 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