Lossless HTTP message values for later component derivation.
new/1 accepts a map. Required for both kinds: :kind (:request or
:response), ordered :fields of RequestSeal.FieldOccurrence, :trailers,
a RequestSeal.Body, and :transport (RequestSeal.TransportFacts). Trailers
are an ordered list (including an observed empty list), :unavailable, or
:pending while the body is streaming. A streaming body describes unread
caller-owned content, not transport progress: a completed capture may have
known trailers while its content stream remains unread. Header and trailer
sections never mix.
Total fields and trailers are bounded to 1,024 occurrences and 1,048,576 bytes
of names and values. Field maps, tuples, and invalid nested structs reject.
Requests require a token :method (at most 256 bytes), :raw_target (at most
16,384 bytes), and explicit :target_form: :origin, :absolute, :authority
(CONNECT with a port), or :asterisk (OPTIONS). URI octets and percent-encoding
remain unchanged; fragments, userinfo, malformed escapes, and controls reject.
Absolute targets require a scheme and authority. Optional :scheme and
:authority are a pair of caller-declared authoritative origin facts, never
inferred from Host or Forwarded. Scheme is bounded to 64 bytes; authority to
1,024 bytes. Ports are decimal numbers in 0..65535. Missing origin is preserved
as nil; a later component rule decides whether it is required. When supplied,
an absolute target must match the declared scheme and authority byte for byte;
a CONNECT target must match the declared authority. No normalization is used
to reconcile conflicting declarations.
Responses require integer :status in 100..599. Optional :related_request
is a validated request (not another response); absent context remains nil.
Request-only fields must be nil on responses. Requests cannot carry status
or another related request. Unknown options reject; no normalization occurs.
validate/1 applies the same checks to directly constructed or modified structs.
Success establishes a well-formed value, not HTTP framing, component availability,
cryptographic validity, or authentication. Signature-base derivation is provided by RequestSeal.SignatureBase.
Generic signing and verification are provided by RequestSeal; optional Req/Finch
adapters, Plug integration (also used by Phoenix), Ash scope mapping, and Web Bot Auth protocol-00
are implemented. Other named application profiles use RequestSeal.Profile
in extension packages. This module starts no process and reads no stream.
Default inspection is redacted.
iex> {:ok, body} = RequestSeal.Body.new(%{state: :unavailable})
iex> {:ok, transport} = RequestSeal.TransportFacts.new(%{})
iex> {:ok, request} = RequestSeal.Message.new(%{kind: :request, method: "GET", raw_target: "/a%2Fb?", target_form: :origin, fields: [], trailers: :unavailable, body: body, transport: transport})
iex> {request.raw_target, request.body.state, request.scheme}
{"/a%2Fb?", :unavailable, nil}
iex> RequestSeal.Message.validate(request)
:ok
iex> RequestSeal.Message.new(%{kind: :request})
{:error, %RequestSeal.Message.Error{reason: :invalid_message}}
Summary
Functions
Construct a request or response; return a bounded error on invalid input.
Build a request from an absolute HTTP(S) URL, ordered headers, and exact body bytes.
Build a response with ordered headers and retained bytes (nil means empty).
Validate all nested values, states, bounds, targets, and request linkage.
Types
@type t() :: %RequestSeal.Message{ authority: binary() | nil, body: RequestSeal.Body.t(), fields: [RequestSeal.FieldOccurrence.t()], kind: :request | :response, method: binary() | nil, raw_target: binary() | nil, related_request: t() | nil, scheme: binary() | nil, status: 100..599 | nil, target_form: :origin | :absolute | :authority | :asterisk | nil, trailers: [RequestSeal.FieldOccurrence.t()] | :unavailable | :pending, transport: RequestSeal.TransportFacts.t() }
Functions
@spec new(map()) :: {:ok, t()} | {:error, RequestSeal.Message.Error.t()}
Construct a request or response; return a bounded error on invalid input.
@spec request(binary(), binary(), [{binary(), binary()}], binary() | nil, keyword()) :: {:ok, t()} | {:error, RequestSeal.Message.Error.t()}
Build a request from an absolute HTTP(S) URL, ordered headers, and exact body bytes.
Scheme and host are lowercased; default ports (HTTPS 443 and HTTP 80)
are omitted, matching Finch and Req. Percent escapes and query bytes
are preserved. An absent path becomes /; an empty query retains its ?.
Header case, order, and repeats remain unchanged. nil and "" both retain
empty content. Transport declarations stay unknown and trailers unavailable.
All values pass through new/1, including its 1 MiB body retention limit.
The only option is :digest, default nil: a nonempty unique list of
"sha-256" and/or "sha-512" adds Content-Digest over the exact bytes.
Existing Content-Digest headers reject when generating a digest; omit the
option to preserve caller-supplied headers. Invalid options return
:invalid_message; invalid headers and bodies retain their existing errors.
iex> {:ok, message} = RequestSeal.Message.request("post", "https://example.com:8443/a%2Fb?", [{"X", "one"}, {"X", "two"}], nil)
iex> {message.method, message.authority, message.raw_target, message.body.bytes}
{"post", "example.com:8443", "/a%2Fb?", ""}
iex> Enum.map(message.fields, &{&1.name, &1.value})
[{"X", "one"}, {"X", "two"}]
@spec response(100..599, [{binary(), binary()}], binary() | nil, keyword()) :: {:ok, t()} | {:error, RequestSeal.Message.Error.t()}
Build a response with ordered headers and retained bytes (nil means empty).
Accepts the same :digest option as request/5. Optional :request links a
validated request for components with req; no linkage is inferred. Status,
fields, body, and related request pass through new/1 unchanged.
iex> {:ok, request} = RequestSeal.Message.request("GET", "https://example.com/", [], nil)
iex> {:ok, response} = RequestSeal.Message.response(204, [], nil, request: request)
iex> {response.status, response.related_request == request, response.body.bytes}
{204, true, ""}
@spec validate(term()) :: :ok | {:error, RequestSeal.Message.Error.t()}
Validate all nested values, states, bounds, targets, and request linkage.