parse-net 0.9.1

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

Features

The list below is the plan for 1.0. What ships in the current version is under Status.

Field grammar

  • Byte classes - every ABNF class (tchar, qdtext, ctext, etagc, attr-char, MIME token, RFC 3986, Structured Field keys and tokens) in one 1 KiB table, one load and one mask per byte.
  • Lexical forms - tokens, OWS (space and tab only), quoted strings with zero-copy access and buffer-free comparison, nested comments without recursion.
  • Lists and parameters - quote- and comment-aware comma lists, several field lines read as one list without copying, parameters that refuse duplicates by default.

Field values

  • Media types and negotiation - media types, RFC 2046 boundaries, exact q-values, Accept, Accept-Language (RFC 4647), Accept-Encoding, Accept-Charset.
  • Dates - HTTP-date in all three formats with the RFC 9110 50-year rule, IMF-fixdate formatting, RFC 5322 date-time; years 1 to 9999.
  • Caching and conditions - entity tags, If-Match, If-None-Match, Range, Content-Range, If-Range, Cache-Control.
  • Downloads and links - Content-Disposition with RFC 8187 decoding, Link with title*.
  • Proxies and auth - Forwarded, X-Forwarded-* with a trust predicate that fails closed, Authorization, WWW-Authenticate.
  • Hosts and origins - Host with real IPv4 and IPv6 validation, Origin with same-origin comparison.
  • Structured Fields - RFC 9651 Items, Lists, Dictionaries, every bare item type, parse and serialize.
  • Email - folding, RFC 2047 encoded words in Q and B, MIME parameters with RFC 2231 continuations, addr-spec, msg-id.

Reliability and safety

  • No unsafe code - #![forbid(unsafe_code)]; the SWAR scanners are plain safe Rust.
  • No allocation, no recursion - proved by a counting allocator and depth tests.
  • Bounded work - every limit checked before the work it governs; linear time on every input.
  • Tested to the byte - unit tests on every path, property tests, fuzz targets for every parser, RFC vectors and the httpwg Structured Field suite.

Note on performance: the target is at least 20% faster than the best Rust competitor for each comparable operation, at equal or stronger strictness. The measurements, the method, and the commands to reproduce them are in docs/PERFORMANCE.md.


Installation

Add to your Cargo.toml:

[dependencies]
parse-net = "0.9.0"

# Optional features:
parse-net = { version = "0.9.0", features = [
    "alloc",        # owned forms and Sink for Vec<u8>
    "std",          # SystemTime conversions and http_date() with the system clock
] }

Feature recommendations:

  • Minimal (embedded, engines): no features; no_std, no allocator.
  • Servers: std.
  • Owned values: alloc.

Quick Start

The convention is use parse_net as pn;.

use parse_net as pn;

// Media types: one strict view, no copy.
let mt = pn::parse_media_type(b"text/html; charset=UTF-8")?;
assert!(mt.essence_is("text/html"));
assert!(mt.charset().is_some_and(|c| c.eq_ignore_ascii_case(b"utf-8")));

// Lists: commas inside quoted strings and comments do not split.
let codings: Vec<&[u8]> = pn::list(b"gzip, deflate;q=0.5, x=\"a,b\"")?.iter().collect();
assert_eq!(codings, [&b"gzip"[..], b"deflate;q=0.5", b"x=\"a,b\""]);

// HTTP-date: all three formats in, IMF-fixdate out.
let now = 1_792_540_800; // the caller's clock, Unix seconds
let d = pn::http_date_at(b"Sunday, 06-Nov-94 08:49:37 GMT", now)?;
assert_eq!(&d.format(), b"Sun, 06 Nov 1994 08:49:37 GMT");

// Host: real IPv4 and IPv6 validation, ports up to 65535.
let hp = pn::parse_host_port(b"[2001:db8::1]:8443")?;
assert_eq!(hp.port, Some(8443));
assert!(pn::parse_host_port(b"127.1").is_err()); // read as IPv4 by browsers: refused

// Writers cannot emit CR, LF, or NUL.
let mut buf = [0u8; 64];
let mut out = pn::SliceSink::new(&mut buf);
pn::media::write("text", "plain", &[("charset", "utf-8")], &mut out)?;
assert_eq!(out.written(), b"text/plain; charset=utf-8");
assert!(pn::write::write_field_value(b"x\r\nSet-Cookie: y", &mut out).is_err());
# Ok::<(), Box<dyn std::error::Error>>(())

Errors carry a kind and an offset, never input bytes:

use parse_net as pn;

let err = pn::parse_media_type(b"text/ html").unwrap_err();
assert_eq!(err.kind(), pn::ErrorKind::InvalidMediaType);
assert_eq!(err.to_string(), "invalid media type at offset 5");

The rest of Tier 1 arrives with 0.4.0 and 0.5.0 (see the roadmap):

pn::best_media(accept, &["text/html", "application/json"])    // Option<usize>
pn::if_none_match(if_none_match, &current_etag)?              // bool: answer 304
pn::byte_ranges(range, file_len, &mut buf)?                   // RangeOutcome
pn::parse_content_disposition(value)?.filename()              // prefers filename*
pn::response_cache_control(value)?                            // ResponseDirectives
pn::client_address(peer, pn::ForwardedSource::XForwardedFor(&lines), &trusted, 16)?
pn::sfv::parse_item(b"same-origin")?                          // Sec-Fetch-Site
pn::bearer(authorization)?                                    // Option<&[u8]>

What it is not

  • Not a protocol engine: message framing, start lines, and chunked bodies belong to http-net, h2-net, fcgi-net, and ws-net.
  • Not a header map: the engine owns field storage.
  • Not a URL parser (url-lib) or a base64 codec (encode-lib).
  • Not cookie grammar (cookie-net) or a full email parser.
  • Not a policy engine: it reports facts strictly; what to accept is the caller's call.

Status: feature complete

Current Version: 0.9.0 - beta: every planned field, for HTTP and for mail, benchmarked, reviewed, hardened, and in use by published consumers: http-net, cookie-net, and form-net. Target: 1.0.0 stable, per the roadmap in dev/ROADMAP.md.

What's complete:

  • Errors and limits - ParseError (8 bytes: kind and offset, never input bytes), ErrorKind, WriteError, Limits with RFC-sourced defaults, DuplicatePolicy.

  • Byte classes and scanning - every ABNF class in one 1 KiB table; SWAR byte search and field-value validation, eight bytes per step.

  • Writers - Sink, SliceSink, CountSink, Vec<u8> (with alloc); tokens, quoted strings, words, parameters, field values, numbers; nothing half-written after an error.

  • Lexical forms - tokens, OWS, quoted strings (zero-copy, buffer-free comparison), words, comments (nested, counted, never recursed).

  • Lists and parameters - quote- and comment-aware lists, several field lines as one list without copying, parameters that refuse duplicates by default, and RFC 6455 extension parameters whose value may be left out.

  • Media types - parsing, RFC 2046 boundaries, charset, suffixes, writing, canonical form.

  • HTTP-date - all three formats, the RFC 9110 50-year rule with an explicit clock, IMF-fixdate formatting, years 1 to 9999, the calendar functions; SystemTime conversions with std.

  • Host - reg-names, IPv4, IPv6, IPvFuture, ports; numeric names that browsers read as IPv4 refused.

  • Percent-encoding - validation, decoding into a caller buffer (UTF-8 checked, lowercase-only for Structured Fields), encoding with named sets.

  • Content negotiation - Accept, Accept-Language (RFC 4647 lookup or filtering), Accept-Encoding, Accept-Charset, with q-values and an explicit outcome when a header is ignored.

  • Conditional and range requests - entity tags with strong and weak comparison, If-Match, If-None-Match, Range resolution that refuses abusive sets, Content-Range, If-Range.

  • File names - RFC 8187 values and Content-Disposition with filename* preferred, decoded controls refused, path separators and bidi controls reported; every tc2231 case has a verdict.

  • Caching, proxies, auth, origins - Cache-Control, Forwarded and X-Forwarded-* with client-address resolution through trusted proxies only, Authorization and WWW-Authenticate with redacted Debug, Origin with exact same-origin comparison.

  • Structured Fields - RFC 9651 Items, Lists, and Dictionaries with every bare item type: views, a single-pass visitor, and a canonical writer, passing the httpwg parsing and serialisation suites in full.

  • Links - RFC 8288 Link values, with the first rel winning and title* decoded.

  • Email - RFC 5322 dates, header folding and unfolding, RFC 2047 encoded words (Q and B, read and written), MIME parameters with RFC 2231 continuations, addr-spec, mailboxes, and message identifiers, for smtp-net and login validation.

  • Owned forms - with alloc, owned media types, entity tags, host-ports, and Structured Field values that give the same views back.

  • Proof - unit tests on every path, property tests (no panic, round trips, writer-parser closure, combined lines), counting-allocator tests for every parse and write, RFC and tc2231 vectors, 37 fuzz targets including four differential oracles against httpdate, mime, sfv, and base64, and an example for every public item.

  • Performance - at least 25% faster than the competing Rust crates on every compared operation (25% to 81%), without allocating: see docs/PERFORMANCE.md.

  • Hardening - an attack test for every threat in the model, every walker checked for linear time at 1 KiB, 64 KiB, and 1 MiB, and every fuzz target run for 24 hours: see docs/SECURITY.md.

What's planned next:

  • 0.9.x: ws-net, smtp-net, and web-net on parse-net, then the release candidates.
  • 1.0.0: the full audit in docs/STABILITY-1.0.md, released on James's go; what stands open is in dev/READINESS-1.0.md.

Note on API stability: the API was locked at 0.2.0 (docs/API.md); names and signatures change only with a recorded decision until 1.0.0 freezes them, with the contract recorded in docs/STABILITY-1.0.md.


Documentation & Resources

  • API Reference - every public item with examples
  • Grammar - each field's ABNF, case rules, and leniencies
  • Architecture - modules, the byte-class table, views, and algorithms
  • Performance - benchmarks against the competing crates, and the method
  • Security - the threat model, its tests, and how to report a vulnerability
  • Platform notes - features, no_std and bare-metal builds, stack use
  • Migration - moving http-net and ws-net onto parse-net
  • Plan - design, threats, and decisions
  • Roadmap - versions and exit criteria
  • Changelog - every change
  • docs.rs - rendered API documentation
  • crates.io - the crate

Version Compatibility

  • Rust: 1.85+ (edition 2024)
  • MSRV Policy: after 1.0.0, an MSRV raise is a major change
  • API Stability: pre-1.0; the API may change at each minor version until 1.0.0
  • Targets: anything with core; thumbv7em-none-eabi is checked in CI

Development Setup

git clone https://github.com/jamesgober/parse-net.git
cd parse-net
cargo test --all-features
cargo clippy --all-targets --all-features -- -D warnings

Contributing

See dev/DIRECTIVES.md for engineering standards and the definition of done. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.