-
Notifications
You must be signed in to change notification settings - Fork 0
Guide WebSockets
ARO provides built-in WebSocket support for real-time bidirectional communication. WebSocket connections are established via HTTP Upgrade on the same port as the HTTP server.
WebSocket enables persistent, full-duplex communication channels. Unlike HTTP request-response cycles, WebSocket allows servers to push data to clients without polling.
Use cases:
- Real-time updates (dashboards, notifications)
- Chat applications
- Live feeds (logs, sensor data, market data)
- Collaborative features (multi-user editing)
WebSocket shares the HTTP port via HTTP Upgrade:
HTTP Client ARO Application
| |
| GET /ws HTTP/1.1 |
| Upgrade: websocket |
|-----------------------------------> |
| |
| HTTP/1.1 101 Switching Protocols |
|<----------------------------------- |
| |
| <-- WebSocket frames (bidirectional) |
| |
Three events manage the WebSocket lifecycle:
| Event | Triggered When |
|---|---|
| Connect | Client completes WebSocket handshake |
| Message | Client sends a text message |
| Disconnect | Connection closes |
Use the WebSocket Event Handler business activity pattern:
(Handler Name: WebSocket Event Handler)
The handler name determines which event triggers it:
- Contains "Connect" → connection events
- Contains "Message" → message events
- Contains "Disconnect" → disconnection events
(Handle WebSocket Connect: WebSocket Event Handler) {
<Extract> the <connection-id> from the <event: id>.
<Extract> the <path> from the <event: path>.
<Log> "WebSocket client connected" to the <console>.
<Return> an <OK: status> for the <connection>.
}
Event properties:
| Property | Type | Description |
|---|---|---|
id |
String | Unique connection identifier |
path |
String | WebSocket path (e.g., "/ws") |
remoteAddress |
String | Client IP address |
(Handle WebSocket Message: WebSocket Event Handler) {
<Extract> the <message> from the <event: message>.
<Extract> the <connection-id> from the <event: connectionId>.
<Log> "Received message" to the <console>.
<Return> an <OK: status> for the <message>.
}
Event properties:
| Property | Type | Description |
|---|---|---|
message |
String | Text message content |
connectionId |
String | Sender's connection ID |
(Handle WebSocket Disconnect: WebSocket Event Handler) {
<Extract> the <connection-id> from the <event: connectionId>.
<Extract> the <reason> from the <event: reason>.
<Log> "WebSocket client disconnected" to the <console>.
<Return> an <OK: status> for the <disconnection>.
}
Event properties:
| Property | Type | Description |
|---|---|---|
connectionId |
String | Connection that closed |
reason |
String | Close reason (if provided) |
Send a message to all connected WebSocket clients:
<Broadcast> the <message> to the <websocket>.
Messages are automatically serialized to JSON if they are objects.
(postMessage: Chat API) {
<Extract> the <text> from the <request: body message>.
<Create> the <message> with {
text: <text>,
createdAt: <now>
}.
<Store> the <message> into the <message-repository>.
(* Push to all connected clients *)
<Broadcast> the <message> to the <websocket>.
<Return> a <Created: status> with <message>.
}
WebChat/
├── openapi.yaml # API contract
├── main.aro # Application lifecycle
├── api.aro # HTTP route handlers
├── websocket.aro # WebSocket event handlers
└── templates/
└── index.html # Chat UI
(Application-Start: Web Chat) {
<Log> "Starting Web Chat..." to the <console>.
<Start> the <http-server> with {}.
<Log> "Server ready on http://localhost:8080" to the <console>.
<Log> "WebSocket available on ws://localhost:8080/ws" to the <console>.
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Log> "Shutting down..." to the <console>.
<Stop> the <http-server> with {}.
<Return> an <OK: status> for the <shutdown>.
}
(homePage: Web Chat API) {
<Transform> the <html> from the <template: index.html>.
<Return> an <OK: status> with <html>.
}
(getMessages: Web Chat API) {
<Retrieve> the <messages> from the <message-repository>.
<Return> an <OK: status> with <messages>.
}
(postMessage: Web Chat API) {
<Extract> the <body> from the <request: body>.
<Extract> the <text: message> from the <body>.
<Create> the <message: Message> with {
message: <text>,
createdAt: <now>
}.
<Store> the <message> into the <message-repository>.
<Broadcast> the <message> to the <websocket>.
<Return> a <Created: status> with <message>.
}
(Handle WebSocket Connect: WebSocket Event Handler) {
<Log> "WebSocket client connected" to the <console>.
<Return> an <OK: status> for the <connection>.
}
(Handle WebSocket Disconnect: WebSocket Event Handler) {
<Log> "WebSocket client disconnected" to the <console>.
<Return> an <OK: status> for the <disconnection>.
}
openapi: 3.0.3
info:
title: Web Chat API
version: 1.0.0
paths:
/home:
get:
operationId: homePage
/messages:
get:
operationId: getMessages
post:
operationId: postMessage// Connect to WebSocket
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const ws = new WebSocket(`${protocol}//${location.host}/ws`);
ws.onopen = () => {
console.log('Connected');
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
displayMessage(message);
};
ws.onclose = () => {
console.log('Disconnected, reconnecting...');
setTimeout(connect, 3000);
};
// Post via HTTP, receive updates via WebSocket
function postMessage(text) {
fetch('/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'message=' + encodeURIComponent(text)
});
}By default, WebSocket connections are accepted on /ws:
const ws = new WebSocket('ws://localhost:8080/ws');| Feature | WebSocket | TCP Socket |
|---|---|---|
| Protocol | WebSocket (RFC 6455) | Raw TCP |
| Port | HTTP port via Upgrade | Dedicated port |
| Framing | Message-based | Stream-based |
| Browser support | Native WebSocket API | Not supported |
| Use case | Web real-time apps | Backend services |
| Handler pattern | WebSocket Event Handler |
Socket Event Handler |
Choose WebSocket for browser-based real-time features. Use TCP sockets for backend-to-backend communication or when you need a dedicated port.
(Handle WebSocket Connect: WebSocket Event Handler) {
<Extract> the <id> from the <event: id>.
<Extract> the <remote> from the <event: remoteAddress>.
<Log> "Client connected: " to the <console>.
<Log> <remote> to the <console>.
<Return> an <OK: status> for the <connection>.
}
function connect() {
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onclose = () => {
console.log('Disconnected, reconnecting in 3s...');
setTimeout(connect, 3000);
};
ws.onmessage = (event) => {
handleMessage(JSON.parse(event.data));
};
}
connect();The recommended pattern is:
- Client sends commands via HTTP POST
- Server broadcasts updates to all clients via WebSocket
This separates concerns and ensures reliable command delivery.
- WebSocket uses SwiftNIO's WebSocket implementation
- Available on macOS and Linux (not Windows)
- JSON serialization is automatic for broadcast messages
- Default path is
/ws
- Sockets - TCP socket communication
- Events - Event-driven patterns
- HTTP Services - HTTP server setup
Fundamentals
- The Basics
- Feature Sets
- Actions
- Variables
- Type System
- Control Flow
- Error Handling
- Computations
- Dates
- Concurrency
Runtime & Events
I/O & Communication
Advanced