# `SSL`
[🔗](https://github.com/gsmlg-dev/http_fetch/blob/v0.21.18/apps/ex_ssl/lib/ssl.ex#L1)

OTP `:ssl`-compatible client facade for the implemented `ex_ssl` feature subset.

This experimental client supports TLS 1.3 and explicit bounded TLS 1.2, binary raw sockets, passive and
active-once delivery, application ownership transfer, authenticated ALPN,
bounded streaming writes, and peer verification by default. These restricted
defaults differ from OTP. Unsupported options return explicit
`{:error, {:options, reason}}` errors.

STARTTLS callers must own a passive binary/raw TCP socket, fully consume and
validate the application's upgrade response, and reject any buffered plaintext.
Supply `server_name_indication` as the certificate reference DNS name, or use
`ex_ssl: [reference_identity: {:ip, address} | {:dns_id, name}]` to verify a
separate IP/DNS identity independently of SNI and the TCP destination. Once
upgrade is attempted, an owned socket is closed on failure; plaintext must
never resume. A socket belonging to another process is left untouched.

# `abandon_send`

```elixir
@spec abandon_send(SSL.Socket.t()) :: :ok | {:error, term()}
```

Permanently abandons application writes while retaining response reads.

Only the controlling owner may call this on an established connection. An
admitted send settles as `{:error, :write_abandoned}`; unsent plaintext is discarded and
future sends are rejected. A bounded already-protected application output job may
still finish its original transport write under its original finite deadline.
The job may include an automatic KeyUpdate followed by one application record. It is
never re-encrypted or replayed. At most one already-protected mandatory control
response may wait behind it, retaining committed traffic-key epochs and the
original finite deadline. Reads keep their existing byte bounds. Explicit close,
cancellation and owner loss abort both outputs.

Call `close/1` after consuming the response, or to abort the pending record.
Ordinary sender death continues to fail the connection closed unless the owner
explicitly abandons writes before the sender dies.

# `close`

```elixir
@spec close(SSL.Socket.t()) :: :ok | {:error, term()}
```

Closes the connection. Closing an already closed handle succeeds.

# `connect`

```elixir
@spec connect(:gen_tcp.socket(), list()) :: {:ok, SSL.Socket.t()} | {:error, term()}
```

# `connect`

```elixir
@spec connect(term(), term(), term()) :: {:ok, SSL.Socket.t()} | {:error, term()}
```

# `connect`

```elixir
@spec connect(term(), :inet.port_number(), list(), timeout()) ::
  {:ok, SSL.Socket.t()} | {:error, term()}
```

# `connect_prepared`

```elixir
@spec connect_prepared(:gen_tcp.socket(), SSL.PreparedUpgrade.t(), timeout()) ::
  {:ok, SSL.Socket.t()} | {:error, term()}
```

Upgrades an owned passive binary/raw TCP socket with a prepared snapshot.

Uses the resolved policy without rereading files or resolving options/profiles.
Ownership, pending-plaintext rejection, timeout and close-on-failure semantics
are the same as `connect/3`. Future runtime or handshake failures remain possible.

# `connect_prepared`

```elixir
@spec connect_prepared(
  :gen_tcp.socket(),
  SSL.PreparedUpgrade.t(),
  keyword(),
  timeout()
) ::
  {:ok, SSL.Socket.t()} | {:error, term()}
```

Attaches lifecycle authority to one prepared upgrade, without overriding policy.

# `connection_information`

```elixir
@spec connection_information(SSL.Socket.t()) :: {:ok, keyword()} | {:error, term()}
```

Returns the supported non-secret connection metadata.

# `connection_information`

```elixir
@spec connection_information(SSL.Socket.t(), term()) ::
  {:ok, keyword()} | {:error, term()}
```

# `controlling_process`

```elixir
@spec controlling_process(SSL.Socket.t(), pid()) :: :ok | {:error, term()}
```

Transfers application ownership while the TLS process retains the TCP socket.

# `enable_duplex_reads`

```elixir
@spec enable_duplex_reads(SSL.Socket.t()) :: :ok | {:error, term()}
```

Enables bounded response reads during an application send.

This owner-only, idempotent operation applies to an established open socket.
It requires a finite send timeout and rejects later infinite-timeout sends.
Reads may progress while one already-protected application output job is
pending. At most one already-protected mandatory control response may wait
behind that job under the original absolute write deadline. Handshake, control
and shutdown output barriers remain in place; waiting grants no new budget.
Normal sender death still fails the connection closed.

# `negotiated_protocol`

```elixir
@spec negotiated_protocol(SSL.Socket.t()) :: {:ok, binary()} | {:error, term()}
```

Returns the authenticated ALPN selection, if the server negotiated one.

# `peercert`

```elixir
@spec peercert(SSL.Socket.t()) :: {:ok, binary()} | {:error, term()}
```

Returns the authenticated server leaf certificate as DER.

# `peername`

```elixir
@spec peername(SSL.Socket.t()) ::
  {:ok, {:inet.ip_address(), :inet.port_number()}} | {:error, term()}
```

Returns the live TCP peer address and port.

# `prepare_upgrade`

```elixir
@spec prepare_upgrade(term()) :: {:ok, SSL.PreparedUpgrade.t()} | {:error, term()}
```

Resolves and validates TLS configuration for a later TCP upgrade.

Loads trust and client credentials and freezes the upgrade reference, SNI and
concrete wire profile. No socket, connection process or ephemeral keys are
allocated. The returned opaque snapshot can be reused deliberately; connection
randomness, ephemeral keys and session-ticket binders are generated at attach.
This configuration snapshot is not an attempt/adoption or cleanup receipt.

# `recv`

```elixir
@spec recv(SSL.Socket.t(), non_neg_integer(), timeout()) ::
  {:ok, binary()} | {:error, term()}
```

Receives available bytes for length 0, or exactly length bytes. Timeout retains buffered data.

# `send`

```elixir
@spec send(SSL.Socket.t(), iodata()) :: :ok | {:error, term()}
```

Writes iodata as one ordered logical write. Concurrent writes return `:busy`.

# `send`

```elixir
@spec send(SSL.Socket.t(), iodata(), {:abs_ms, integer()}) :: :ok | {:error, term()}
```

Writes iodata under an absolute monotonic millisecond deadline.

The deadline includes reservation and every protected record of this logical
write. The controlling owner's configured send timeout may shorten it. This
operation does not change the socket's owner or configured timeout.

# `setopts`

```elixir
@spec setopts(SSL.Socket.t(), list()) :: :ok | {:error, term()}
```

Changes supported delivery, send, and mutable TCP options after validation.

# `sockname`

```elixir
@spec sockname(SSL.Socket.t()) ::
  {:ok, {:inet.ip_address(), :inet.port_number()}} | {:error, term()}
```

Returns the live local TCP address and port.

---

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