# `HTTP.FetchOptions`
[🔗](https://github.com/gsmlg-dev/http_fetch/blob/v0.21.17/apps/http_fetch/lib/http/fetch_options.ex#L1)

Options processing for `HTTP.fetch/2` requests.

`HTTP.fetch/2` accepts a flat init keyword list or map, mirroring the browser
`fetch(input, init)` shape. Supported fetch-style options are:

- `method` - nonempty ASCII HTTP method token, defaulting to `GET`. Common
  methods normalize to their conventional atoms; other binary methods retain
  their case, and atom methods are uppercased on the wire. DAV methods such as
  `:propfind` and `:report` carry bodies normally. Invalid token bytes reject
  before network I/O; extension strings are never converted into new atoms.
- `headers` - request headers as a list, map, or `HTTP.Headers`
- `body` - request body
- `duplex` - request streaming mode; `:half` or `"half"` for streaming bodies
- `signal` - `HTTP.AbortController` PID
- `redirect` - `:follow`, `:manual`, or `:error`; defaults to `:follow`

The socket transport also accepts Elixir-specific extensions:

- `telemetry` - emit request and stream telemetry; defaults to `true`. Shared
  pool/connection counters require the global opt-out in `HTTP.Telemetry`.
- `decode_body` - decode response Content-Encoding; defaults to `true`. Set
  `false` to preserve stored entity bytes for buffered and streamed HTTP/1,
  HTTP/2, and HTTP/3 responses without changing headers.
- `stream_response` - set `true` to resolve at final response headers and
  expose an acknowledged body stream, even for small Content-Length bodies.
  Defaults to `false` (automatic size/framing policy). HEAD and bodyless
  statuses remain buffered empty responses. The request timeout still bounds
  the entire response; callers can observe headers before applying a body deadline.
- `request_mode` - `:fetch` (default) or `:proxy`; proxy mode retains request
  entities for all admitted methods, including GET, DELETE, and HEAD
- `content_type` - convenience Content-Type value for request bodies
- `timeout` - request timeout in milliseconds or `:infinity` for HTTP/1
- `error_mode` - `:raw` (default) or `:structured`; structured failures return
  `HTTP.RequestError` with conservative pre-send evidence. No automatic retry.
- `read_idle_timeout` - positive milliseconds (1..4,294,967,295), or `nil`
  to disable (default). Bounds each pending HTTP/1 transport read, including
  headers; nonempty transport data resets the clock. Consumer acknowledgement
  backpressure pauses reads and this clock. Writes do not reset it.
- `write_idle_timeout` - positive milliseconds (same range), or `nil` to disable
  (default). Bounds each at-most-16KiB HTTP/1 transport write; each successful
  nonempty send starts a fresh write budget. This is transport acceptance,
  not peer acknowledgement or a partial-syscall progress guarantee. Waiting
  for an upload source does not consume a write budget. Shorter existing
  `socket_opts[:send_timeout]` and overall request limits still apply.
  Both idle options support ordinary HTTP/1 TCP, OTP TLS and ExSSL, including
  HTTP/1 reuse. They require `http_version: :http1` (the default); automatic/H2/H3
  protocols and managed `transport_scope` are rejected. Timeouts report
  `:read_idle_timeout` / `:write_idle_timeout` (or `HTTP.RequestError` in
  structured mode before response headers). Neither changes connection or
  overall request deadlines or the cleanup completion contract.
- `connect_timeout` - connection timeout in milliseconds or `:infinity`
- `response_stream_idle_timeout` - ordinary HTTP/1 response-stream inactivity
  in positive milliseconds (1..4,294,967,295), or `:infinity` to disable the
  network-wait clock. `nil` (default) retains the global streaming timeout.
  Consumer acknowledgement waits use the separate ACK policy below.
  Convenience body readers use the same stream policy. Other protocols and
  managed `transport_scope` reject explicit policies.
- `response_stream_ack_timeout` - ordinary HTTP/1 response chunk delivery and
  consumer acknowledgement budget in positive milliseconds (1..4,294,967,295),
  or `:infinity`. `nil` (default) retains the global streaming timeout.
  This covers a buffered chunk awaiting a reader and a delivered chunk awaiting
  acknowledgement. It does not change network idle, overall request deadlines,
  cancellation or reader-death cleanup. Other protocols and managed
  `transport_scope` reject explicit policies.
- `transport_scope` - opaque `HTTP.ManagedTransport` generation selecting
  private reusable HTTP/1 or HTTP/2 transports. Requires raw streamed responses,
  manual/error redirects, and the origin/policy frozen by `open/1`.
- `connect_address` - caller-validated literal IPv4/IPv6 tuple to dial instead
  of resolving the URL hostname. Keeps original HTTP authority, TLS SNI and
  certificate hostname verification. No DNS/address fallback or request replay.
  Requires `redirect: :manual` or `:error`; validate every permitted redirect
  hop independently and provide that hop's pin. HTTP/3, Unix sockets and
  proxies are unsupported. Pools isolate each pin from DNS and other targets.
  Cancellation and request/connect deadlines cover establishment. Conflicting
  TLS SNI rejects before I/O; native verification and trust defaults remain.
  With `:ex_ssl`, a literal-IP HTTPS URL must match the pin, since a distinct
  original IP verification identity is currently unsupported.
- `http_version` - protocol selection, one of `:http1`, `:http2`, `:http3`,
  `:h2c`, or `:auto`; defaults to `:http1`
- `tls_backend` - TLS implementation, `:ssl` or `:ex_ssl`; defaults to the
  shared `:http_core, :tls_backend` configuration
- `ssl` - TLS options passed to the selected TLS backend
- `socket_opts` - socket options passed to the underlying transport
- `unix_socket` - Unix Domain Socket path
- `http1_reuse` - opt in to HTTP/1 keep-alive; requires `http_version: :http1`.
  Only fully consumed, self-delimited responses are reusable. Failed or
  cancelled requests are closed, and requests are never retried on stale peers.
- `http1_scope` - optional atom or string separating caller routes sharing an origin.
- `http1_pool_size` - retained idle sockets per route/policy, 1..16 (default 2).
- `http1_idle_timeout` - idle retention in milliseconds, 1..60,000 (default 30,000).
  All callers share a maximum of 256 idle sockets. TLS identities, socket
  options, isolation scope and route policies are part of the reuse key;
  opaque callbacks disable reuse. HTTP/1 reuse defaults to `false`.

- `proxy` - explicit proxy `{:http, host, port, opts}` or
  `{:https, host, port, opts}`. Options are
  `headers: [{"Proxy-Authorization", value}]` and a finite positive `timeout`.
  HTTP/1 cleartext uses absolute-form forwarding; HTTPS uses CONNECT and then
  origin TLS/ALPN. H2 over HTTPS is supported. H2c, HTTP/3, Unix socket routes,
  and ExSSL IP-origin tunnels are rejected explicitly. Environment/NO_PROXY
  selection belongs to the caller.
  HTTPS proxies support HTTP origins only, using TLS to the proxy and
  absolute-form HTTP/1 forwarding. `ssl` and `tls_backend` apply to the proxy;
  its hostname is the certificate/SNI identity. TLS-over-TLS origins are
  rejected. Proxy scheme, endpoint, credentials, and TLS policy isolate pools.
- `http2_profile` - versioned HTTP/2 wire profile (only used by HTTP/2)
- `http2_reuse` - whether an HTTP/2 connection may be reused; defaults to `true`
- `http3_profile` - QUIC wire profile; defaults to `:ordered`
- `http3_reuse` - whether an HTTP/3 connection may be reused; defaults to `true`
- `http2_scope` - non-sensitive caller isolation scope
- `http2_priority` - per-request HTTP/2 priority metadata

# `http_version`

```elixir
@type http_version() :: :http1 | :http2 | :http3 | :h2c | :auto
```

# `redirect`

```elixir
@type redirect() :: :follow | :manual | :error
```

# `t`

```elixir
@type t() :: %HTTP.FetchOptions{
  body: any(),
  connect_address: :inet.ip_address() | nil,
  connect_timeout: timeout() | nil,
  content_type: String.t() | nil,
  decode_body: boolean(),
  duplex: :half | nil,
  error_mode: :raw | :structured,
  headers: HTTP.Headers.t(),
  http1_idle_timeout: pos_integer(),
  http1_pool_size: pos_integer(),
  http1_reuse: boolean(),
  http1_scope: atom() | String.t() | nil,
  http2_priority: map() | keyword() | nil,
  http2_profile: atom() | String.t() | map() | nil,
  http2_reuse: boolean(),
  http2_scope: atom() | String.t() | nil,
  http3_profile: atom() | map() | nil,
  http3_reuse: boolean(),
  http_version: http_version(),
  method: HTTP.Request.method(),
  proxy: {:http | :https, String.t(), :inet.port_number(), keyword()} | nil,
  read_idle_timeout: pos_integer() | nil,
  redirect: redirect(),
  request_mode: HTTP.Request.request_mode(),
  response_stream_ack_timeout: pos_integer() | :infinity | nil,
  response_stream_idle_timeout: pos_integer() | :infinity | nil,
  signal: any() | nil,
  socket_opts: list() | nil,
  ssl: list() | nil,
  stream_response: boolean(),
  telemetry: boolean(),
  timeout: timeout() | nil,
  tls_backend: tls_backend(),
  transport_scope: term(),
  unix_socket: String.t() | nil,
  write_idle_timeout: pos_integer() | nil
}
```

# `tls_backend`

```elixir
@type tls_backend() :: term()
```

# `get_body`

```elixir
@spec get_body(t()) :: any()
```

Extracts body from options.

# `get_content_type`

```elixir
@spec get_content_type(t()) :: String.t() | nil
```

Extracts content type from options.

# `get_duplex`

```elixir
@spec get_duplex(t()) :: :half | nil
```

Extracts the request body streaming mode from options.

# `get_headers`

```elixir
@spec get_headers(t()) :: HTTP.Headers.t()
```

Extracts headers from options.

# `get_method`

```elixir
@spec get_method(t()) :: HTTP.Request.method()
```

Extracts the HTTP method from options.

# `new`

```elixir
@spec new(map() | keyword() | t()) :: t()
```

Creates a new FetchOptions struct from a flat map, keyword list, or existing
FetchOptions struct.

# `to_transport_options`

```elixir
@spec to_transport_options(t()) :: keyword()
```

Converts fetch init options to the internal socket transport option list.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
