simple-http-server 0.8.0

Simple HTTP server
# Axum Version Analysis

## Goal

The goal of this rewrite was not to mechanically translate the old `iron` code, but to replace it with an `axum` implementation while keeping the CLI and the main runtime behavior aligned as closely as practical, with a smaller direct dependency surface.

The default direct dependencies are:

- `axum 0.8.8`
- `clap 4.6.0`
- `tokio 1.50.0`
- `flate2 1.1.9`
- `mime_guess 2.0.5`
- `httpdate 1.0.3`
- `base64 0.22.1`
- `time 0.3.47`

When the `tls` feature is enabled, it additionally pulls in:

- `openssl 0.10.76`
- `tokio-openssl 0.6.5`

`clap` intentionally does not use `derive`, and TLS is intentionally behind a single `tls` feature that is disabled by default.

## Benefits

- The server now sits on a maintained async stack instead of the old `iron` ecosystem.
- The direct dependency surface is smaller and easier to reason about.
- TLS uses `axum::serve` with a custom listener instead of adding `axum-server`.
- The codebase is split into focused modules for config, server setup, handlers, and utilities.
- The default build does not carry OpenSSL or TLS glue code.

## Tradeoffs and Pitfalls

### 1. TLS is optional, but when enabled it uses system OpenSSL

To keep the default build smaller, TLS is disabled unless `tls` is enabled.

Impact:

- Default builds are smaller and compile faster.
- TLS-capable builds depend on system OpenSSL compatibility.
- `--cert` still uses PKCS#12, which keeps CLI behavior aligned but keeps TLS on the OpenSSL path.

### 2. File responses are streamed

Regular file responses and compressed file responses are streamed instead of buffering the entire file.

Impact:

- Memory usage is much lower for large files.
- Compressed responses still use a background thread per streamed file response.

### 3. Uploads are streamed to temporary files

Uploads are written to temporary files first, then moved into place after CSRF validation succeeds.

Impact:

- Large uploads do not need to sit fully in memory.
- Failed CSRF validation cleans up temporary files.

### 4. `base-url` is normalized

`--base-url` is normalized to start with `/`, and to end with `/` when it is not root.

Impact:

- Inputs like `prefix`, `/prefix`, and `/prefix/` all normalize to `/prefix/`.

### 5. `--try-file` semantics are explicit

- Relative paths are resolved against the server root.
- Absolute paths are used as-is.

This is more explicit than the old behavior and matches the current help text.

### 6. HEAD + compression is cleaner, not identical

The old middleware-based compression path had some inconsistent historical behavior for `HEAD`.

The current implementation does not preserve those quirks:

- `HEAD` does not read and compress the whole file just to compute a compressed length.
- The result is cleaner HTTP behavior, but not byte-for-byte historical parity in that edge case.

## Compatibility Summary

### Kept aligned

- CLI flags and short options
- Directory listing
- `--index`
- `--upload` / `--csrf`
- `--auth`
- `--redirect`
- `--nocache`
- `--norange`
- `--cors`
- `--coop` / `--coep`
- `--compress`
- `--try-file`
- `--base-url`
- `--cert` / `--certpass` with PKCS#12

### Intentional fixes

- `--open` uses `https://` when TLS is enabled
- Error pages escape injected text
- `--cors` responds to `OPTIONS` preflight requests
- `--upload-size-limit` supports human-readable values such as `30K`, `50M`, and `1G`, interpreted with powers of 1024

## Verification

The current replacement is covered by black-box HTTP tests for:

- directory listing
- `try-file`
- basic auth
- range requests
- cache revalidation
- CORS preflight
- upload with CSRF
- gzip compression

Both default and `tls` builds pass `cargo test` and `cargo clippy`.