The Alusus HTTP module now includes comprehensive WebSocket support, built on top of the CivetWeb library. WebSockets provide full-duplex communication channels over a single TCP connection, enabling real-time bidirectional communication between clients and servers.
- Real-time Communication: Bidirectional messaging between client and server
- Event-driven Architecture: Callback-based handling for connection events
- Text and Binary Messages: Support for both text and binary data transmission
- Connection Management: Automatic handling of WebSocket handshake and connection lifecycle
- Integration: Seamless integration with existing HTTP server functionality
def WebSocketConnectCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Int];
Called when a new WebSocket connection is established. Return 0 to accept the connection, non-zero to reject.
def WebSocketReadyCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Void];
Called when the WebSocket connection is ready for communication.
def WebSocketDataCallback: alias ptr[func (connection: ptr[Connection], bits: Int, data: CharsPtr, dataLen: Int, userData: ptr[Void]): Int];
Called when data is received from the client. The bits parameter indicates the frame type:
1: Text frame2: Binary frame8: Close frame9: Ping frame10: Pong frame
def WebSocketCloseCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Void];
Called when a WebSocket connection is closed.
func setWebSocketHandler(
context: ptr[Context],
uri: CharsPtr,
connectHandler: WebSocketConnectCallback,
readyHandler: WebSocketReadyCallback,
dataHandler: WebSocketDataCallback,
closeHandler: WebSocketCloseCallback,
userData: ptr[Void]
): Void;
Registers WebSocket handlers for a specific URI path.
Parameters:
context: Server context returned bystartServer()uri: URI path for WebSocket endpoint (e.g., "/websocket")connectHandler: Callback for new connectionsreadyHandler: Callback when connection is readydataHandler: Callback for incoming messagescloseHandler: Callback when connection closesuserData: Optional user data passed to callbacks
func setWebSocketHandlerWithSubprotocols(
context: ptr[Context],
uri: CharsPtr,
subprotocols: ptr[CharsPtr],
connectHandler: WebSocketConnectCallback,
readyHandler: WebSocketReadyCallback,
dataHandler: WebSocketDataCallback,
closeHandler: WebSocketCloseCallback,
userData: ptr[Void]
): Void;
Same as setWebSocketHandler but with support for WebSocket subprotocols.
func writeToWebSocket(connection: ptr[Connection], opcode: Int, data: CharsPtr, dataLen: Int): Int;
Send raw WebSocket frame with specified opcode.
Opcodes:
1: Text frame2: Binary frame8: Close frame9: Ping frame10: Pong frame
func writeTextToWebSocket(connection: ptr[Connection], data: CharsPtr, dataLen: Int): Int;
Send text message to WebSocket client.
func writeBinaryToWebSocket(connection: ptr[Connection], data: CharsPtr, dataLen: Int): Int;
Send binary message to WebSocket client.
func closeWebSocket(connection: ptr[Connection]): Int;
Close WebSocket connection gracefully.
import "Srl/Console.alusus";
import "Srl/String.alusus";
import "Apm.alusus";
Apm.importFile("Alusus/Http");
module WebSocketExample {
use Srl;
func start() {
// Start HTTP server
def context: ptr[Http.Context] = Http.startServer(handleHttpRequest~ptr, "8080");
// Register WebSocket handler
Http.setWebSocketHandler(
context,
"/websocket",
onConnect~ptr,
onReady~ptr,
onData~ptr,
onClose~ptr
);
Console.print("Server listening on port 8080\n");
Console.getChar();
Http.stopServer(context);
};
func handleHttpRequest(connection: ptr[Http.Connection]): Int {
def req: ptr[Http.RequestInfo] = Http.getRequestInfo(connection);
if String.isEqual(req~cnt.localUri, "/websocket") return 0;
// Handle regular HTTP requests
Http.print(connection, "HTTP/1.1 200 OK\r\n");
Http.print(connection, "Content-Type: text/html\r\n\r\n");
Http.print(connection, "<h1>WebSocket Server</h1>");
return 1;
};
func onConnect(connection: ptr[Http.Connection], userData: ptr[Void]): Int {
Console.print("WebSocket client connected\n");
return 0; // Accept connection
};
func onReady(connection: ptr[Http.Connection], userData: ptr[Void]): Void {
Console.print("WebSocket ready\n");
Http.writeTextToWebSocket(connection, "Welcome!", 8);
};
func onData(connection: ptr[Http.Connection], bits: Int, data: CharsPtr, dataLen: Int, userData: ptr[Void]): Int {
if bits == 1 { // Text frame
Console.print("Received: ");
Console.print(data, dataLen);
Console.print("\n");
// Echo message back
Http.writeTextToWebSocket(connection, data, dataLen);
}
return 1;
};
func onClose(connection: ptr[Http.Connection], userData: ptr[Void]): Void {
Console.print("WebSocket client disconnected\n");
};
};
WebSocketExample.start();
const ws = new WebSocket('ws://localhost:8080/websocket');
ws.onopen = function(event) {
console.log('Connected to WebSocket server');
ws.send('Hello Server!');
};
ws.onmessage = function(event) {
console.log('Received:', event.data);
};
ws.onclose = function(event) {
console.log('Connection closed');
};
ws.onerror = function(error) {
console.log('WebSocket error:', error);
};- Error Handling: Always check return values from WebSocket functions
- Resource Management: Properly handle connection cleanup in close callbacks
- Message Validation: Validate incoming message data before processing
- Connection Limits: Consider implementing connection limits for production use
- Security: Implement proper authentication and authorization for WebSocket endpoints
- Connection Refused: Ensure the server is running and the port is accessible
- Handshake Failed: Check that the WebSocket endpoint URI is correctly registered
- Message Not Received: Verify that the data callback is properly handling the message type
- Connection Drops: Implement ping/pong handling for connection keep-alive
- Use browser developer tools to inspect WebSocket frames
- Add logging in callback functions to trace connection lifecycle
- Check server logs for handshake and connection errors
- Verify that the CivetWeb library has WebSocket support compiled in