# `Cartouche.HTTP`
[🔗](https://github.com/zenhive/cartouche/blob/main/lib/cartouche/http.ex#L1)

HTTP helpers shared by cartouche's `Req`-based RPC transports.

# `normalize_response`

```elixir
@spec normalize_response({:ok, Req.Response.t()} | {:error, Exception.t() | term()}) ::
  {:ok, Req.Response.t()} | {:error, Req.Response.t() | String.t()}
```

Normalizes the result of a `Req` request.

Any non-2xx status code is wrapped in `{:error, response}`. Transport and other client
errors are abstracted into a `{:error, message}` string so callers never see Req's
internals.

> #### Transport-error contract {: .info}
>
> A `%Req.TransportError{}` (connection closed/refused/timeout) maps to
> `"[Cartouche] HTTP client error: <reason>"`. This is a deliberate 0.5.0 contract:
> under the old Finch transport the analogous mock surfaced as a bare
> `"[Cartouche] Unknown error: :closed"`, but a real transport failure is an HTTP
> client error and is reported as one.

# `req_options`

```elixir
@spec req_options(module(), Keyword.t(), Keyword.t()) :: Keyword.t()
```

Builds the `Req.request/1` option list for a transport call.

Merges, in increasing precedence:

  1. `base` — the transport-built options (`:method`, `:url`, `:headers`, `:body`,
     `:receive_timeout`, plus `decode_body: false` and `retry: false`).
  2. `config :cartouche, <owner>, [...]` — per-transport options keyed by the calling
     module (`Cartouche.RPC`, `Cartouche.Solana.RPC`, or the hidden OpenChain API
     transport). This
     is where test/consumer code injects a stub `:plug`; production leaves it unset.
  3. `config :cartouche, :req_options, [...]` — the global production seam (a custom
     Finch pool, retries, telemetry, proxies, …).
  4. `Keyword.get(call_opts, :req_options, [])` — per-call overrides (highest), e.g.
     `req_options: [plug: nil]` to bypass a configured stub and hit the network.

---

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