HTTP.ManagedTransport (http_runtime v0.21.16)

Copy Markdown View Source

Owns one bounded transport generation for a fixed origin and literal address.

open/1 freezes direct TCP/OTP TLS policy and creates private connection pools. Pass the opaque scope as transport_scope to raw, streamed Fetch requests with manual/error redirects. Request and connection capacity fail immediately; request leases last through unread response streams.

retire/2 closes admission and idle connections. Graceful retirement permits admitted requests to finish; abort retirement also cancels them. Its durable receipt can be awaited repeatedly from any process. :ok proves termination of this generation's independently monitored requests, connectors, raw socket ports, owners, private pools, supervisor and coordinator. A finite wait can return :cleanup_pending; lost evidence returns :cleanup_unconfirmed. Creator death initiates abort retirement. No request is replayed. Success is logical resource retirement, not a certificate of native/deferred timer, monitor, alias or signal-buffer reclamation, allocator/OS memory return, or a numerical native-memory budget. Missing or mismatched native accounting certification cannot be replaced by a snapshot or retirement receipt.

Opening and selecting a generation

{:ok, scope} = HTTP.ManagedTransport.open(
  origin: "https://upstream.example",
  connect_address: {192, 0, 2, 10},
  http_version: :http2,
  max_requests: 100,
  max_connections: 2,
  idle_timeout: 30_000
)

promise = HTTP.fetch("https://upstream.example/data",
  transport_scope: scope,
  redirect: :manual,
  decode_body: false,
  stream_response: true
)

origin, literal connect_address and explicit http_version are required. Supported protocols are :http1 (HTTP/HTTPS), :http2 (HTTPS) and :h2c (HTTP). Optional capacity is max_requests: 1..2048 (default 100), max_connections: 1..256 (default 2), max_pending: 0, and idle_timeout: 1..60000 milliseconds (default 30000). ssl and socket_opts freeze supported OTP TLS/socket settings. Only tls_backend: :ssl and http2_profile: :native_v1 are accepted. TLS files and system CAs are captured once at open; rotated policy requires a new scope. For explicit HTTPS :http1, ssl: [alpn_advertised_protocols: ["http/1.1"]] optionally advertises the single fixed binary protocol. Omitting it preserves no ALPN advertisement. Other ALPN lists, server ALPN settings and explicit ALPN overrides for managed H2 are rejected; H2 retains its protocol-selected "h2" advertisement. See the managed transport guide for the supported SSL option keys.

Request capacity includes preparation and unread streams. Connections include connecting, checked-out and idle transports. Exhaustion returns {:error, {:transport_scope_capacity, :requests | :connections}}, with no waiting queue. Frozen TLS/socket policy owns compact binary backing within its 1 MiB serialized limit. Freeze/preparation copy overlap and OTP's parsed TLS state are outside that retained-policy payload bound. Request metadata is bounded to 64 KiB/256 fields, buffered upload bodies to 1 MiB, and upload chunks/bridges to 64 KiB. The native HTTP/2 writer/receive budgets are 1 MiB each per connection, header blocks are bounded to 64 KiB/256 fields/256 frames. The H2 owner admits at most 128 informational heads/64 KiB regular metadata over each request lifetime, before enqueueing. Each request generates at most 130 header notifications including final headers/trailers. SETTINGS pool snapshots coalesce with one in flight; GOAWAY draining notices are one-shot. See the managed transport guide for accounted-data, representation and decode/copy overlap reservations. Caller-owned producer input, arbitrary public-operation messages, OS/TLS allocations and the whole VM are outside these retained transport bounds.

Conflicting origin, route, TLS or socket overrides fail before dialing. Each request/connect deadline remains independent; socket writer settings stay frozen. Managed upload attachment and admission use the request deadline. An unresponsive caller source receives cooperative stop but keeps cleanup pending and its slot occupied until the caller terminates it.

Status and retirement

{:ok, status} = HTTP.ManagedTransport.snapshot(scope, 100)
{:ok, receipt} = HTTP.ManagedTransport.retire(scope, mode: :graceful)
:ok = HTTP.ManagedTransport.await_retired(receipt, 1_000)

Snapshot and receipt waits require finite, nonnegative millisecond timeouts. Snapshots contain aggregate counts, finite limits, policy digest and lifecycle, without request history or credentials. Retirement closes admission and idle sockets immediately; :graceful drains live requests, while :abort requests cancellation. Neither a response terminal event nor an individual HTTP.RequestCompletion proves whole-generation retirement. A caller may retry receipt waits after {:error, :cleanup_pending}. Lost evidence produces {:error, :cleanup_unconfirmed}. Normal coordinator termination preserves the durable receipt; unexpected coordinator death cannot report success.

OTP TLS connections retain independent receiver and sender termination evidence after pool handoff. If TLS setup fails without returning a socket, or its resource representation is unsupported, the generation retires with unconfirmed cleanup; open a new scope for subsequent requests.

Summary

Types

t()

@opaque t()

Functions

await_retired(receipt, timeout)

@spec await_retired(HTTP.ManagedTransport.Receipt.t(), non_neg_integer()) ::
  :ok | {:error, term()}

open(opts)

@spec open(keyword()) :: {:ok, t()} | {:error, term()}

retire(scope, opts \\ [])

@spec retire(t(), keyword()) ::
  {:ok, HTTP.ManagedTransport.Receipt.t()} | {:error, term()}

snapshot(scope, timeout)

@spec snapshot(t(), non_neg_integer()) :: {:ok, map()} | {:error, term()}