RequestSeal.Plug.Capture (RequestSeal v0.2.0)

Copy Markdown View Source

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

t()

@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

read_body(conn, opts)

@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.