parse-net 0.9.2

Grammar for Internet message fields: RFC 9110 field syntax, media types, RFC 9651 Structured Fields, HTTP and RFC 5322 dates, negotiation, entity tags, ranges, Content-Disposition, Cache-Control, Forwarded, auth challenges, and the email lexical layer. no_std, no allocation, sans-IO, zero dependencies.
Documentation
# parse-net v0.3.0 - Core

**Date:** 2026-10-22
**Compare:** [`v0.2.0...v0.3.0`](https://github.com/jamesgober/parse-net/compare/v0.2.0...v0.3.0)

**The first release with code: the grammar http-net, ws-net, and cookie-net need.** v0.3.0 shipped the error and limit types, the byte-class table, the scanners, the writers, tokens and quoted strings, lists and parameters, media types, HTTP-date, `Host`, and percent-encoding, all `no_std` without an allocator, with zero dependencies and no `unsafe`. Both planned consumers were moved onto it in trial branches before the release: their full test suites passed, ws-net's handshake got 23% cheaper, and http-net stayed within its 5% budget.

## Highlights

- One strict, allocation-free implementation of the RFC 9110 field grammar that every protocol crate can share, built against the API locked in 0.2.0.
- Validate-then-view everywhere: a parse checks the whole value once and returns a `Copy` view; iterators over it cannot fail.
- Errors are 8 bytes, carry a kind and an offset, and never contain input bytes, so logging one cannot inject text into a log line.
- Writers validate before they write: none can emit CR, LF, or NUL, and a `SliceSink` never holds half a value after an error.
- A differential fuzzing oracle against `httpdate` and `mime` found four disagreement classes on its first run; every one has a written verdict.

## Added

- **Errors and limits.** `ParseError` with `kind`, `offset`, `line`, and `new`; `ErrorKind` (49 variants, each documented with what it refuses and what to do); `WriteError`; `Full`. `Limits` holds every bound with a const setter, and its defaults meet each RFC 9651 minimum, so a conforming sender is never refused by default. `DuplicatePolicy` refuses repeated parameter names unless a field's RFC says first or last wins.
- **Byte classes.** One 1 KiB table, built at compile time from the ABNF, holds 25 classes (`tchar`, `qdtext`, `ctext`, `etagc`, `attr-char`, the MIME `token`, the RFC 3986 classes, the Structured Field key and token characters, `token68`, base64, `atext`, RFC 2046 boundary characters, and the cookie octets cookie-net needs). Each predicate is one load and one mask; every class was tested for all 256 bytes against a slow reference written from the RFC text.
- **Scanning.** `find_byte`, `find_either`, and `check_field_value` read eight bytes per step with no `unsafe`; property tests showed them equal to the bytewise definitions. `check_field_value` reports CR, LF, NUL, and other controls as distinct kinds, so a smuggling attempt is told apart from a typo.
- **Writers.** `Sink`, `SliceSink`, `CountSink` (for exact sizing), and `Sink for Vec<u8>` with `alloc`; `write_token`, `write_quoted` (escaping only `"` and `\`), `write_word`, `write_param`, `write_field_value` (the header-injection guard), and the number writers moved from http-net.
- **Lexical forms.** Tokens, OWS (SP and HTAB only; VT, FF, and NBSP are never stripped), quoted strings with zero-copy access, buffer-free comparison and `unescape_into`, words that compare equal across the token and quoted forms, and nested comments counted against a depth limit, never recursed.
- **Lists and parameters.** Comma lists that never split inside a quoted string or a comment, with a budget on empty elements; several field lines read as one list without copying; parameters with case-insensitive names, refusal of duplicates by default, and a `Debug` that prints names only.
- **Media types.** `parse_media_type` and `media::parse` with `Wildcard` refused in a concrete type; RFC 2046 boundary validation; `charset`, suffixes, `is_json`, `is_form_urlencoded`, `is_multipart_form_data`; `media::write` and `write_canonical`.
- **HTTP-date.** All three formats, case-sensitive names, weekday checked, only `GMT`; RFC 850 two-digit years resolved by the RFC 9110 50-year rule against a clock the caller passes (`http_date_at`), or the system clock with `std` (`http_date`); IMF-fixdate formatting over years 1 to 9999; the calendar functions; `SystemTime` conversions.
- **Host.** Registered names, canonical dotted-decimal IPv4, every IPv6 text form, IPvFuture, ports up to 65535; userinfo refused first; names that browsers read as IPv4 (`127.1`, `0x7f.1`, `example.123`) refused, which removes a known disagreement between WHATWG and RFC 3986. `host::parse_uri_host_port` parses the same grammar without stripping whitespace, for request targets and URLs.
- **Percent-encoding.** Validation, decoding into a caller buffer with nothing written on error (any case, UTF-8 checked, or lowercase only for Structured Fields), and encoding with named sets that url-lib can extend.
- **docs/MIGRATION.md** for the http-net and ws-net maintainers: each function that moves, its replacement, and every behaviour that changes.

## Testing

- Unit tests at the bottom of every source file, covering each `Ok` and `Err` path, and integration tests in `tests/`: property tests (no parser panics on arbitrary bytes, every writer's output parses back to its input, SWAR equals bytewise, several lines equal the joined value), counting-allocator tests proving no parse and no write into a `SliceSink` allocates, RFC vectors stored as data files, and compile-time `Send + Sync + Unpin` and size checks.
- Ten fuzz targets: `text_quoted`, `text_comment`, `list`, `params`, `media_type`, `http_date`, `host`, `pct_decode`, and the differential oracles `diff_http_date` and `diff_media_type`. Each target asserts no panic, no allocation, and no input bytes in error text, and checks its round trip. Each ran 60 seconds clean in WSL Ubuntu (1.3 to 13.7 million inputs per target; the oracles 120 seconds, 18.9 and 32.8 million).
- Every example in `docs/API.md` for a shipped item was compiled and run against the build.

## Security

- The differential oracle recorded four classes where `httpdate` and parse-net disagree, all with the verdict "ours right" (`tests/differential/verdicts.md`): RFC 850 years from 70 to 76 (`httpdate` uses a fixed pivot that RFC 9110 rules out), dates before 1970, OWS around the value, and VT, FF, CR, or LF around the value, which `httpdate` strips and parse-net refuses.
- The http-net migration trial found that validating a request target with a field-value parser would accept `CONNECT  example.com:443`; `host::parse_uri_host_port` was added so that cannot happen, and the migration guide says which to use where.

## Performance

The consumer gate (no consumer more than 5% slower after adopting parse-net) was measured as instructions per iteration with callgrind, because criterion wall-clock runs on the development machines varied by up to 15% between identical runs:

| Workload | Before | After | Change |
|---|---|---|---|
| http-net `request_head` | 8,569 | 8,905 | +3.9% |
| http-net `response_write` | 2,335 | 2,345 | +0.4% |
| http-net `response_parse` | 2,972 | 2,974 | +0.1% |
| ws-net `handshake/from_head` | 43,348 | 33,261 | -23.3% |

The first list walker took 81 ns for three short browser lists against 21 ns for http-net's unvalidated split; it was rewritten as one pass with one table lookup per byte (about 50 ns), and `list::for_each` validates and visits in a single pass (about 30 ns). Competitor benchmarks come in `0.6.0`.

## Compatibility

- MSRV 1.85, edition 2024. `no_std` without `alloc` is the base build and was built for `thumbv7em-none-eabi`.
- Zero runtime dependencies in every feature combination; `proptest` is a dev-dependency only.
- Pre-1.0: the API follows `docs/API.md` and changes only with a recorded decision.

## Verification

- Local gate on Windows and WSL Ubuntu, Rust 1.99 and 1.85: format, clippy with default, all, and no default features under `-D warnings`, tests in the same three modes, examples, documentation with default and all features under `-D warnings`, the bare-metal build, and `cargo package` in a fresh target directory.
- Banned-content and link checks over every shipped file.
- CI green on Linux, macOS, and Windows, stable and 1.85, before the tag.

## What's next

`0.4.0`: content negotiation, entity tags and conditional requests, ranges, RFC 8187 and `Content-Disposition`, `Cache-Control`, `Forwarded` with trusted-proxy resolution, `Authorization`, `Origin`, and Structured Field parsing, with the httpwg structured-field-tests suite and `docs/GRAMMAR.md`. The draft pull requests that move http-net and ws-net onto parse-net open now that 0.3.0 is published.

**Full Changelog:** https://github.com/jamesgober/parse-net/compare/v0.2.0...v0.3.0