# `SSL`
[🔗](https://github.com/gsmlg-dev/http_fetch/blob/v0.21.17/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 mandatory peer verification. 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. Reads keep their existing byte bounds; inbound
protocol traffic that requires output while this record is pending fails closed.

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()}
```

# `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. Handshake, control and shutdown output barriers remain in place;
inbound protocol traffic requiring output on a busy writer fails closed.
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.

# `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`.

# `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*
