# WebSocket Proxy

A lightweight HTTP-to-WebSocket proxy server that allows plain HTTP clients to connect to WebSocket servers. This proxy is protocol-agnostic and can be used to establish WebSocket connections for any application that cannot directly support WebSockets.

## Features

- **Protocol-Agnostic**: Works with any WebSocket server, not tied to specific protocols
- **Simple HTTP API**: Connect, send, poll, and disconnect via straightforward HTTP endpoints
- **Session Management**: Automatic cleanup of inactive sessions after 90 seconds
- **Message Buffering**: Unbounded queue to prevent message loss, returns all pending messages in single poll
- **Compression Support**: Automatic per-message compression using context takeover mode
- **Logging**: Logs all operations to stdout for debugging and monitoring

## Architecture

The proxy maintains in-memory sessions, each consisting of:

- A WebSocket connection to the upstream server
- An unbounded outbound message queue for messages received from the server
- Session metadata (ID, target URL, last activity time)

A background goroutine reads messages from the WebSocket and appends them to the queue. Clients retrieve all pending messages via polling, which returns immediately if messages are available or waits up to 20 seconds for new messages to arrive.

## Session Management

- Sessions remain active as long as they receive activity (send or poll) within 90 seconds
- Inactive sessions are automatically cleaned up every 60 seconds
- Message queue is unbounded - grows as needed to prevent message loss
- All pending messages are returned in a single poll response

## Error Handling

All HTTP responses follow a consistent pattern:

- **2xx responses (200, 204)**: Normal operation
- **5xx responses**: Exogenous infrastructure issues, retryable without modification
- **4xx responses (except 404)**: Client usage faults, should be considered fatal
- **404 responses**: Connection broken, possibly by external factors; client must establish replacement connection

This convention is universal across all API endpoints and provides clear guidance for building robust client abstractions.

## Prerequisites

- Go 1.19 or later
- [nhooyr.io/websocket](https://github.com/nhooyr/websocket) library (automatically fetched)

## Building

```bash
make
```

Builds are intended to be **reproducible** by default (works best on Go 1.20 or later).  Users who can't build/run directly from source can verify pre-packaged binary builds by comparing file hashes against hashes from one or more other people who can build from source.  When binary builds are present, see the `go-version.txt` file to find the version used to build them.

Note that pre-built binaries will only be provided for certain "popular" platforms on which users are expected to have the most difficulty building from source, to keep pre-packaged download sizes small.

## Running

Find the `websockproxy-*` file for your platform and run it in a terminal, or `make run` to run directly from source code.

The proxy listens on port `9839` by default.

## API Endpoints

### POST /connect

Establishes a new WebSocket connection.

**Parameters:**
- `target` (query param, required): The WebSocket URL to connect to (e.g., `ws://example.com/socket`)

Additional headers (e.g. auth) are not currently supported; the proxy is intended for protocols (e.g. Archipelago) that handle auth through the websocket itself.

**Response:**
```json
{
  "session": "a1b2c3d4"
}
```

**Example:**
```bash
curl "http://localhost:9839/connect?target=ws://archipelago.gg:38281"
```

### POST /send

Sends a message to the WebSocket server.

**Parameters:**
- `session` (query param, required): The session ID from `/connect`

**Body:** Raw message data to send (typically JSON)

**Response:** HTTP 204 (no content) on success

**Example:**
```bash
curl -X POST "http://localhost:9839/send?session=a1b2c3d4" \
  -H "Content-Type: application/json" \
  -d '{"cmd": "Connect", "name": "Player1"}'
```

### GET /poll

Retrieves messages from the WebSocket server (long polling). Returns **all pending messages** in a single response.

**Parameters:**
- `session` (query param, required): The session ID from `/connect`

**Response:**
```json
{
  "messages": ["message 1", "message 2", "message 3"]
}
```

Returns HTTP 204 (no content) if no message arrives within 20 seconds.

**Example:**
```bash
curl "http://localhost:9839/poll?session=a1b2c3d4"
```

### POST /disconnect

Closes the WebSocket connection.

**Parameters:**
- `session` (query param, required): The session ID from `/connect`

**Response:** HTTP 204 (no content)

**Example:**
```bash
curl -X POST "http://localhost:9839/disconnect?session=a1b2c3d4"
```

## Integration Example

```bash
# Connect
SESSION=$(curl -s "http://localhost:9839/connect?target=ws://example.com/socket" | jq -r .session)

# Send a message
curl -X POST "http://localhost:9839/send?session=$SESSION" \
  -d '{"type": "hello"}'

# Poll for responses (in a loop)
while true; do
  RESPONSE=$(curl -s "http://localhost:9839/poll?session=$SESSION")
  if [ -n "$RESPONSE" ]; then
    # Response contains an array of messages
    echo "Received: $RESPONSE"
    # Process each message in the array
    echo "$RESPONSE" | jq -r '.messages[]' | while read -r msg; do
      echo "Message: $msg"
    done
  fi
done

# Disconnect when done
curl -X POST "http://localhost:9839/disconnect?session=$SESSION"
```
