guides/websocket_guide.md

# WebSocket Guide

hackney provides a WebSocket client with process-per-connection architecture.

## Quick Start

```erlang
{ok, Conn} = hackney:ws_connect(<<"wss://echo.websocket.org">>),
ok = hackney:ws_send(Conn, {text, <<"Hello!">>}),
{ok, {text, <<"Hello!">>}} = hackney:ws_recv(Conn),
hackney:ws_close(Conn).
```

## Connecting

### Simple Connection

```erlang
{ok, Conn} = hackney:ws_connect(<<"wss://example.com/socket">>).
```

### Connection with Options

```erlang
{ok, Conn} = hackney:ws_connect(<<"wss://example.com/socket">>, [
    {connect_timeout, 5000},
    {recv_timeout, 30000},
    {headers, [{<<"authorization">>, <<"Bearer token">>}]},
    {protocols, [<<"graphql-ws">>]}
]).
```

### Available Options

| Option | Default | Description |
|--------|---------|-------------|
| `connect_timeout` | 8000 | TCP connection timeout (ms) |
| `recv_timeout` | infinity | Receive timeout (ms) |
| `headers` | `[]` | Extra headers for upgrade |
| `protocols` | `[]` | Sec-WebSocket-Protocol values |
| `active` | `false` | Active mode: false, true, once |
| `ssl_options` | `[]` | SSL options for wss:// |

## Sending Messages

### Text Messages

```erlang
ok = hackney:ws_send(Conn, {text, <<"Hello">>}).
```

### Binary Messages

```erlang
ok = hackney:ws_send(Conn, {binary, <<1, 2, 3>>}).
```

### Ping/Pong

```erlang
ok = hackney:ws_send(Conn, ping).
ok = hackney:ws_send(Conn, {ping, <<"heartbeat">>}).
```

## Receiving Messages

### Passive Mode (Default)

```erlang
{ok, Frame} = hackney:ws_recv(Conn).
{ok, Frame} = hackney:ws_recv(Conn, 5000).  %% With timeout
```

### Frame Types

```erlang
case hackney:ws_recv(Conn) of
    {ok, {text, Text}} -> handle_text(Text);
    {ok, {binary, Data}} -> handle_binary(Data);
    {ok, ping} -> ok;  %% Auto-responded
    {ok, pong} -> ok;
    {error, {closed, Code, Reason}} -> handle_close(Code)
end.
```

## Active Mode

### Enable Active Mode

```erlang
{ok, Conn} = hackney:ws_connect(URL, [{active, true}]).
%% Or later:
hackney:ws_setopts(Conn, [{active, true}]).
```

### Receive Messages

```erlang
receive
    {hackney_ws, Conn, {text, Text}} -> handle(Text);
    {hackney_ws, Conn, closed} -> done;
    {hackney_ws_error, Conn, Reason} -> error
end.
```

### Active Once

```erlang
{ok, Conn} = hackney:ws_connect(URL, [{active, once}]),
receive {hackney_ws, Conn, Frame} -> ok end,
hackney:ws_setopts(Conn, [{active, once}]).  %% Get next
```

## Closing Connections

```erlang
hackney:ws_close(Conn).
hackney:ws_close(Conn, {1000, <<"Goodbye">>}).
```

### Close Codes

| Code | Meaning |
|------|---------|
| 1000 | Normal closure |
| 1001 | Going away |
| 1002 | Protocol error |

## Example: Chat Client

```erlang
-module(chat).
-export([start/1, send/2]).

start(URL) ->
    {ok, Conn} = hackney:ws_connect(URL, [{active, true}]),
    spawn(fun() -> loop(Conn) end),
    Conn.

send(Conn, Msg) ->
    hackney:ws_send(Conn, {text, Msg}).

loop(Conn) ->
    receive
        {hackney_ws, Conn, {text, Text}} ->
            io:format("~s~n", [Text]),
            loop(Conn);
        {hackney_ws, Conn, closed} ->
            ok
    end.
```

## Error Handling

```erlang
case hackney:ws_connect(URL) of
    {ok, Conn} -> use(Conn);
    {error, {http_error, 401}} -> unauthorized;
    {error, timeout} -> timeout;
    {error, Reason} -> {error, Reason}
end.
```

## Next Steps

- [HTTP Guide](http_guide.md)
- [Getting Started](../GETTING_STARTED.md)