mini-serve 0.7.0

An HTTP server: trie router, middleware, CORS, optional TLS. Built on hyper + tokio.
Documentation
# mini-serve — Development Plan

Coverage target: **≥ 80%** line coverage. The connection-lifecycle fixes need real
socket-level tests (raw TCP, actual timeouts), not just unit tests of pure functions —
budget for that when estimating phase 0.3.0.

## Phase 0.1.0 — MVP: routing + single-traversal dispatch

*Exit criteria: register routes, dispatch by method and path param, one trie walk per
request from the start (not retrofitted later).*

1. **Commit: `App<S>`, `Node`/trie, single-traversal `find()`.** Primary test: register
   `GET /items/:id`, request `/items/42`, assert the extracted param equals `"42"` and
   the handler runs exactly once (instrument a call counter — proving one traversal, not
   the historical three).
2. **Commit: method dispatch + 405 with `Allow`.** Primary test: register only `GET` on
   a path, send `POST`, assert `405` and an `Allow: GET, HEAD` header with the exact
   expected value — not just status-code presence.
3. **Commit: HEAD-via-GET sharing the same response headers.** Primary test: register a
   `GET` handler returning a known `Content-Length`, send `HEAD`, assert the response
   has the *identical* `Content-Length` and an empty body — the regression test for the
   zeroed-length bug.
4. **Commit: percent-decoding + `+`-as-space in routing/query parsing.** Primary test:
   request `/api/%69tems` against a route registered as `/api/items`, assert it matches;
   separately, parse a query string `q=hello+world`, assert the decoded value is
   `"hello world"`.

## Phase 0.2.0 — State sharing + typed extraction

*Exit criteria: state sharing is a refcount bump; path params extract via serde directly.*

5. **Commit: `State::from_arc`, drop the deep-clone-and-rewrap.** Primary test: hold a
   `Weak` reference to the original `Arc<S>`, make several requests, assert the strong
   count only ever reflects in-flight requests — proving no extra `Arc` was allocated
   per request.
6. **Commit: `MapDeserializer`-based `path_params<T>()`.** Primary test: extract a typed
   struct with a `u64` field from path params, assert the parsed value equals the
   expected number — not `is_ok()` — and that an unparseable segment returns a `400`
   with the documented message.

## Phase 0.3.0 — Connection lifecycle hardening

*Exit criteria: TLS handshake timeout, bounded accept-error backoff, loopback-only
ephemeral binds, and shutdown that isn't blocked by a saturated semaphore — each with a
test that exercises the actual failure mode, not just the code path.*

7. **Commit: TLS handshake timeout.** Primary test: open a raw TCP connection to a TLS
   listener and send nothing; assert the connection is closed within the configured
   timeout and the accept loop continues serving other clients.
8. **Commit: bounded accept-error backoff.** Primary test: inject a simulated repeated
   accept error (via a test-only listener wrapper) and assert the loop's CPU-bound retry
   rate is capped — e.g. assert at least N milliseconds elapse between M consecutive
   error iterations, rather than a busy spin.
9. **Commit: loopback-only ephemeral binds.** Primary test: call `bind_ephemeral()`,
   assert the bound address's IP is exactly `127.0.0.1`.
10. **Commit: shutdown-fair permit acquisition.** Primary test (the regression test for
    the hang): saturate `max_connections = 1` with one long-lived in-flight request,
    trigger shutdown, assert the shutdown future completes once that one request
    finishes — proving shutdown isn't starved by the semaphore.

## Phase 0.4.0 — CORS + error-response hardening

*Exit criteria: the credentialed-wildcard bypass is unrepresentable; 5xx bodies never
leak internals.*

11. **Commit: `CorsConfigBuilder::build() -> Result<...>`.** Primary test: attempt to
    build a config with `allow_all_origins: true` and `credentials: true`, assert it
    returns `Err(CorsConfigError::CredentialedWildcard)` in *both* debug and release
    profiles (run the test under both, or assert on a profile-independent code path).
12. **Commit: default error handler sanitizes 5xx, passes through 4xx.** Primary test:
    a handler returns an `Error::internal(..., "raw db connection string leaked")`;
    assert the client-visible body is the generic message while a test-only log sink
    captures the real one. Separate test: a `bad_request` message passes through intact.
13. **Commit: CORS preflight only for real routes.** Primary test: `OPTIONS` with an
    `Origin` header against an unregistered path returns `404`, not `204`.

## Phase 0.5.0 — Routing performance

*Exit criteria: static-segment matching doesn't clone the param map at every node it
merely passes through.*

14. **Commit: borrow-through param passing with truncate-on-backtrack.** Primary test:
    a benchmark or allocation counter shows zero `PathParams` clones for a request that
    matches a route with no dynamic segments, versus at least one per static ancestor
    node before the fix.

## Phase 0.6.0 — Publish

15. **Commit: `err`/`log`/`tls` feature docs + doc comments on every public item.**
16. **Commit: CI**`cargo test`, `cargo test --all-features`, `cargo clippy -- -D warnings`,
    `cargo llvm-cov --fail-under-lines 80`.
17. **Publish `0.6.0`** to crates.io (name confirmed available).