# `Lather.Http.SSLPoolManager`
[🔗](https://github.com/awksedgreep/lather/blob/v1.1.0/lib/lather/http/ssl_pool_manager.ex#L1)

Bounds the number of distinct Finch pools created for custom `:ssl_options`.

`Lather.Http.Transport.post/3` tags each request with
`{:lather_ssl, hash(ssl_opts)}` and starts a dedicated Finch pool on
first use. Without a bound, callers that generate `ssl_options`
dynamically (per-request certs, timestamps, changing versions) would
create an unbounded number of permanent pool processes, exhausting
memory and file descriptors.

This registry tracks `{origin, ssl_hash}` keys in ETS and refuses to
start new pools once `max_ssl_pools` is reached (default `50`,
configurable via `Application.put_env(:lather, :max_ssl_pools, n)`).
When the limit is hit, `claim/2` returns
`{:error, :pool_limit_exceeded}` and the transport layer surfaces a
`:ssl_pool_limit_exceeded` transport error instead of starting a pool.

Callers should reuse stable `ssl_options` (e.g. via
`Lather.Http.Transport.ssl_options/1`) so distinct configurations map
to a small set of pools. Options are canonicalized (keyword order
normalized) before hashing so equivalent configurations share a pool.

# `claim`

```elixir
@spec claim(
  String.t(),
  keyword()
) :: :ok | {:error, :pool_limit_exceeded}
```

Claims a pool slot for `{url, ssl_opts}`.

Returns `:ok` if the pool was already registered or a new slot was
available, or `{:error, :pool_limit_exceeded}` when the registry is at
capacity.

# `count`

```elixir
@spec count() :: non_neg_integer()
```

Returns the number of registered SSL pools.

# `max_pools`

```elixir
@spec max_pools() :: pos_integer()
```

Maximum number of distinct SSL pools allowed.

Reads `Application.get_env(:lather, :max_ssl_pools, 50)`.

# `normalize`

```elixir
@spec normalize(keyword()) :: keyword()
```

Normalizes `ssl_options` into a canonical form for hashing.

Keyword lists are sorted by key (recursively); other terms pass
through unchanged.

# `pool_tag`

```elixir
@spec pool_tag(keyword()) :: {:lather_ssl, non_neg_integer()}
```

Returns the canonical pool tag for the given `ssl_options`.

Options are normalized (keyword order sorted recursively) before
hashing so equivalent configurations share a pool.

# `release`

```elixir
@spec release(
  String.t(),
  keyword()
) :: :ok
```

Releases a previously claimed pool slot (e.g. when pool startup fails).

Slots for successfully started pools are intentionally retained for the
lifetime of the VM: Finch pools are permanent processes, so forgetting
them would re-open the proliferation vector.

# `reset`

```elixir
@spec reset() :: :ok
```

Clears the registry. Intended for tests.

---

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