Capture a bounded request before destructive parsing or origin/IP rewriting.
All options are required: :origin is :connection,
{:declared, scheme, authority}, or {:forwarded, %{trusted_peers: CIDRs, field: :forwarded | :x_forwarded}}; :max_body_bytes is nonnegative;
:read_timeout is 1..300,000 milliseconds. A CIDR is {ip_tuple, prefix}.
Forwarded reconstruction uses the last element only after checking the actual
connection peer against the explicit ranges; connection mode ignores it.
Original ingress facts remain separate. Transport facts stay declared and
describe ingress, never the forwarded origin.
Headers retain Plug's occurrence order and protocol provenance. Plug has
already normalized header names; HTTP/2 servers can join cookie crumbs.
Request trailers are :unavailable; policies requiring them reject.
The target is origin-form reconstructed from request_path and query_string.
It supports path derivation, not exact target evidence. Verify rejects required
or covered @request-target, @target-uri, and @query with
:unsupported_component; SignResponse also rejects those request components.
See RequestSeal.Plug for the tested per-transport rule.
OPTIONS * is retained.
CONNECT and an empty host reject rather than invent an origin.
One Plug.Conn.read_body/2 call captures bytes. :more or a returned body
exceeding the bound halts with 413, without a second read. Other capture
failures halt with 400 and an empty body. Failure is stored as a bounded
RequestSeal.Adapter.Error; invalid options raise that bounded error at init.
Parsed body_params reject with :parser_order before any read or resolver.
The Delivery adapter replays retained bytes to all readers, including
multipart; read_body/2 records invocation and delegates to Plug. Reads honor :length and return
:more until drained, then empty bytes. Negative or non-integer replay
:length returns a bounded :invalid_options adapter error without advancing
the replay. Custom wrapper readers must return
replayed bytes unchanged; their subsequent transformations cannot be observed
by the adapter. Retention remains caller-owned. Only HTTP/1.0 and HTTP/1.1
rejects carry connection: close; HTTP/2 rejects omit connection fields.
Summary
Functions
Record reader invocation and delegate to Plug; replay length-bounded bytes unchanged.
Types
@type t() :: %RequestSeal.Plug.Capture{ ingress: %{ scheme: atom(), host: binary(), port: non_neg_integer(), remote_ip: :inet.ip_address(), protocol: atom() }, message: RequestSeal.Message.t(), origin: %{ scheme: binary(), authority: binary(), source: :connection | :declared | :forwarded } }
Functions
@spec read_body(Plug.Conn.t(), keyword()) :: {:ok | :more, binary(), Plug.Conn.t()} | {:error, term()}
Record reader invocation and delegate to Plug; replay length-bounded bytes unchanged.