RequestSeal.StructuredFields (RequestSeal v0.2.0)

Copy Markdown View Source

Bounded RFC 8941 / RFC 9651 parsing and canonical serialization.

All entry points require a RequestSeal.StructuredFields.Schema. Values retain dictionary and parameter order; duplicates follow the RFC's last-value, first-position rule. No type is authorized by recognizing its syntax. Errors contain only bounded reasons and never return a partially parsed value.

Limits (keyword options) may only lower these ceilings: :max_bytes 65,536 (input or output), :max_members 1,024 (top-level encounters, including duplicates), :max_inner_items 256 (per inner list), :max_parameters 256 (per item/list, including duplicates), :max_key_bytes 256, :max_value_bytes 16,384 (decoded strings/bytes or token bytes), and :max_nodes 4,096 (containers, items, and parameter encounters combined). These are library resource ceilings, not protocol maximums. All limits are positive integers. Grammar depth is fixed: top-level container, optional inner list, item; inner lists cannot nest. Numeric syntax has the RFC's 15-digit integer and 12+3-digit decimal bounds, including leading zeros.

parse_field/5 consumes the existing lossless RequestSeal.Message model, combining matching occurrences in one explicitly selected section in order with comma-space. It never merges headers with trailers or reads a body. Its optional limits apply to both the combined bytes and the complete parse. Missing fields and unavailable sections remain explicit failures.

Serializing an empty list or dictionary returns {:ok, ""}. The empty binary means the caller must omit both the field name and field value from the HTTP message, as specified by RFC 9651 Section 4.1. It must not be emitted as an empty HTTP field. This keeps the serialization result binary while leaving field emission to the caller.

iex> alias RequestSeal.StructuredFields, as: SF
iex> {:ok, schema} = SF.Schema.new(%{revision: :rfc8941, type: :item, item_types: [:integer]})
iex> {:ok, value} = SF.parse("42", schema)
iex> SF.serialize(value, schema)
{:ok, "42"}
iex> SF.parse("@42", schema)
{:error, %RequestSeal.StructuredFields.Error{reason: :revision_type}}

The example integer is RFC 8941 Section 2's Foo-Example. This module performs no network, storage, cryptographic, authentication, or authorization operation.

Summary

Functions

Parse complete ASCII field bytes under an explicit schema and bounded work.

Combine ordered header or trailer occurrences and parse under optional lowered limits.

Serialize canonically; an empty binary means omit the field. Invalid values reject.

Functions

parse(bytes, schema, opts \\ [])

Parse complete ASCII field bytes under an explicit schema and bounded work.

parse_field(message, name, schema, section \\ :headers, opts \\ [])

Combine ordered header or trailer occurrences and parse under optional lowered limits.

:unique_keys optionally lists dictionary keys (or :all) whose repeats reject with :duplicate_key before deduplication, including across field occurrences. It defaults to []; keys must use valid dictionary-key syntax, fit the key byte ceiling, and number no more than :max_members. Other keys retain the last-value, first-position rule. :unique_parameters defaults to false; true rejects repeated parameter names on any member or inner item with :duplicate_parameter, after the bounded parse completes.

serialize(value, schema, opts \\ [])

Serialize canonically; an empty binary means omit the field. Invalid values reject.