4.9 KiB
Realtime WebSocket Broker
web-app-starter includes an optional WebSocket-to-PHP broker under server/realtime-broker/.
The broker is deliberately generic: it owns persistent WebSocket connections, while PHP decides app-specific behavior through a normal HTTP endpoint.
Files
server/realtime-broker/broker.js- broker daemon and importableRealtimeBrokerclassserver/realtime-broker/vendor/ws/- vendoredws8.14.2, MIT licenseserver/realtime-broker/test-broker.js- broker integration testlib/realtime.class.php- PHP helper for backend-initiated commandsviews/api/realtime.php- starter demo backend endpointviews/realtime-demo.php- browser demo pagejs/u-wsconnection.js- small browser connection helper
Start locally
cd /path/to/web-app-starter
REALTIME_HOST=127.0.0.1 \
REALTIME_PORT=8009 \
REALTIME_BACKEND_URL=http://127.0.0.1/web-app-starter/?api/realtime \
REALTIME_COMMAND_TOKEN=change-me \
node server/realtime-broker/broker.js
Then open /?realtime-demo and connect.
Browser URL
In production, proxy /live/ to the broker and let the browser connect to the same origin:
location /live/ {
proxy_pass http://127.0.0.1:8009/live/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}
If the broker is elsewhere, set REALTIME_LIVE_URL or config/settings.php realtime/live_url.
PHP backend protocol
For every browser connect/message/disconnect, the broker POSTs URL-encoded fields to REALTIME_BACKEND_URL:
message: JSON messageconnection: JSON connection/session info
Message types:
session-connectsession-disconnect- browser messages prefixed as
client-*unless already prefixed
The PHP endpoint returns a JSON array of commands.
<?php
$message = json_decode($_POST['message'] ?? '{}', true);
$connection = json_decode($_POST['connection'] ?? '{}', true);
print(json_encode([
['type' => 'session', 'data' => ['room' => 'demo']],
['type' => 'send', 'match' => ['room' => 'demo'], 'message' => ['type' => 'notice', 'text' => 'hello']],
]));
Backend commands
session
Merge data into connection session info. Without match, applies to the current connection.
{"type":"session","data":{"room":"demo","user_id":123}}
send
Send a JSON message to the current connection or matching connections.
{"type":"send","match":{"room":"demo"},"message":{"type":"chat","text":"hello"}}
close
Close the current or matching connections.
{"type":"close","match":{"user_id":123}}
list
Return matching connection sessions to a command caller.
{"type":"list","match":{"room":"demo"}}
log
Log a message if broker logging is enabled.
PHP helper
Realtime wraps command POSTs to the broker:
Realtime::broadcast(['type' => 'notice', 'text' => 'hello'], ['room' => 'demo']);
Realtime::session(['room' => 'demo'], ['user_id' => 123]);
Realtime::connections(['room' => 'demo']);
Realtime::close(['user_id' => 123]);
Configure:
$GLOBALS['config']['realtime'] = [
'broker_url' => 'http://127.0.0.1:8009/',
'command_token' => getenv('REALTIME_COMMAND_TOKEN') ?: '',
];
Command security
If REALTIME_COMMAND_TOKEN is set, command POSTs must include one of:
token=<token>form fieldAuthorization: Bearer <token>X-Realtime-Token: <token>
If no token is set, broker commands are accepted only from REALTIME_COMMAND_ALLOW IPs. Use a token for new deployments.
Broker status
curl http://127.0.0.1:8009/status
Returns JSON counters including active connections, message counts, bytes, backend errors, and denied command attempts.
systemd example
/etc/systemd/system/web-app-starter-realtime.service:
[Unit]
Description=web-app-starter realtime broker
After=network.target
[Service]
Type=simple
WorkingDirectory=/var/www/web-app-starter
Environment=REALTIME_HOST=127.0.0.1
Environment=REALTIME_PORT=8009
Environment=REALTIME_BACKEND_URL=http://127.0.0.1/web-app-starter/?api/realtime
Environment=REALTIME_COMMAND_TOKEN=change-me
ExecStart=/usr/bin/node server/realtime-broker/broker.js
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.target
Validation
node --check server/realtime-broker/broker.js
node --check server/realtime-broker/test-broker.js
node server/realtime-broker/test-broker.js
php -l lib/realtime.class.php
php tests/smoke.php
Notes
- The broker is optional and not started by the PHP app.
- Keep app policy in PHP; keep the broker generic.
- Do not put secrets in messages sent to clients.
- Tune
REALTIME_MAX_MESSAGE_BYTES,REALTIME_MAX_BUFFERED_BYTES, and backend timeout for larger apps.