Change HandleWebSocket to use r.URL.Path as the room identifier instead
of r.URL.Query().Get("room"). This enables clean URL-based room routing
(e.g., ws://host/room-a) without query strings.
Update test helpers (dialTestHub, dialWSWithRoom) to connect via path
segments and fix direct broadcast channel tests to use path-style room
names (with leading slash).
All existing tests pass — clients connecting to / get the default room.
🤖 Assisted by the code-assist SOP
WebSocket Relay Server
A minimal Go WebSocket relay server that broadcasts every incoming message to all connected clients. Supports TLS, Prometheus metrics, configurable logging, and graceful shutdown.
Features
- Fan-out broadcasting — every message is relayed to all connected clients
- TLS support — optional
wss://via cert/key PEM files - Prometheus metrics — connection counts, message totals, disconnections
- Configurable logging — output to stdout, stderr, or file with level filtering
- Graceful shutdown — clean exit on SIGINT/SIGTERM with client notification
- Zero dependencies at runtime — single static binary
Quick Start
# Install dependencies
go mod tidy
# Run the server (defaults to ws://localhost:8443)
make run
# Or with a custom config
go run . --config-file=./config.yaml
Open example/index.html in multiple browser tabs to test the P2P chat demo.
Configuration
Edit config.yaml:
server:
port: 8443
tls:
enabled: false # Set true for wss://
cert_file: cert.pem
key_file: key.pem
metrics:
enabled: true
port: 9090 # Prometheus metrics at :9090/metrics
logging:
output: stderr # stdout, stderr, or a file path
level: info # debug, info, warn, error
Override the config file path with --config-file:
./websocket-relay --config-file=/etc/relay/config.yaml
Logging
The logging section controls where and what the server logs:
| Field | Values | Default | Description |
|---|---|---|---|
output |
stdout, stderr, or a file path |
stderr |
Log output destination |
level |
debug, info, warn, error |
info |
Minimum log level to output |
Examples:
# Log everything to a file
logging:
output: /var/log/websocket-relay.log
level: debug
# Quiet mode — only warnings and errors to stderr
logging:
output: stderr
level: warn
Log messages are prefixed with the level: [DEBUG], [INFO], [WARN], [ERROR].
File output uses append mode (O_APPEND) so logs are preserved across restarts and safe for external log rotation tools.
Usage
Connect any WebSocket client to the server:
const ws = new WebSocket('ws://localhost:8443/');
ws.onmessage = (event) => console.log('Received:', event.data);
ws.onopen = () => ws.send('Hello from client!');
With TLS enabled:
const ws = new WebSocket('wss://localhost:8443/');
All messages sent by any client are broadcast to every connected client (including the sender).
Build
make build # Build binary → build/websocket-relay
make release # Cross-compile linux/amd64 + darwin/arm64
make clean # Remove build artifacts
Testing
make test # Run all tests (unit + integration)
Metrics
When metrics.enabled is true, Prometheus metrics are exposed at http://localhost:9090/metrics:
| Metric | Type | Description |
|---|---|---|
websocket_connected_clients |
Gauge | Currently connected clients |
websocket_messages_total |
Counter | Total messages relayed |
websocket_connections_total |
Counter | Total connections established |
websocket_disconnections_total |
Counter | Total disconnections |
Graceful Shutdown
The server handles SIGINT and SIGTERM signals:
- Stops accepting new connections
- Sends WebSocket
CloseGoingAwayframe to all connected clients - Closes all connections and exits cleanly
Shutdown timeout is 10 seconds.
Project Structure
websocket-relay/
├── main.go # Entry point, signal handling, graceful shutdown
├── internal/
│ ├── config/config.go # YAML config loader
│ ├── hub/hub.go # WebSocket hub, connection management, broadcast
│ ├── logging/logging.go # Log output setup and leveled logger
│ └── metrics/metrics.go # Prometheus metric definitions
├── example/index.html # Browser P2P chat demo
├── config.yaml # Runtime configuration
├── config.example.yaml # Example config with TLS and logging
└── Makefile # Build, test, release commands
License
See repository for license details.