WebSocket Support
Spider includes built-in WebSocket support for real-time bidirectional communication, fully integrated with SpiderServer.
WebSocketHandler
The WebSocketHandler trait provides lifecycle methods for handling WebSocket events:
class EchoWebSocketHandler extends WebSocketHandler {
override def onConnect(connection: WebSocketConnection): Unit = {
println("Client connected")
}
override def onMessage(connection: WebSocketConnection, message: String): Unit = {
connection.sendText(s"Echo: $message")
}
override def onBinary(connection: WebSocketConnection, data: Array[Byte]): Unit = {
connection.sendBinary(data)
}
override def onClose(
connection: WebSocketConnection,
statusCode: Option[Int],
reason: String
): Unit = {
println(s"Connection closed: $statusCode - $reason")
}
override def onError(connection: WebSocketConnection, error: Throwable): Unit = {
error.printStackTrace()
}
}
Lifecycle Methods
onConnect: Called when a WebSocket connection is establishedonMessage: Called when a text message is receivedonBinary: Called when binary data is receivedonClose: Called when the connection is closedonError: Called when an error occurs
WebSocketConnection
The WebSocketConnection class manages the WebSocket connection after the handshake is complete:
// Send text messages
connection.sendText("Hello, client!")
// Send binary data
connection.sendBinary(Array[Byte](1, 2, 3))
// Send ping/pong frames
connection.sendPing()
connection.sendPong()
// Close the connection
connection.close(Some(1000), "Normal closure")
// Check connection state
if (connection.isOpen) {
// Connection is open
}
The connection automatically:
- Responds to ping frames with pong
- Handles message fragmentation
- Manages connection state and lifecycle
Integrating with SpiderServer
WebSockets are integrated directly into SpiderServer via the webSocketRouter parameter:
import dev.alteration.branch.spider.server.*
val webSocketRouter: Map[String, WebSocketHandler] = Map(
"/ws/echo" -> new EchoWebSocketHandler(),
"/ws/chat" -> new ChatWebSocketHandler()
)
val server = new SpiderServer(
port = 8080,
router = httpRouter,
webSocketRouter = webSocketRouter,
config = ServerConfig.default
).withShutdownHook()
server.start()
Example: Chat Server
import scala.collection.mutable
class ChatWebSocketHandler extends WebSocketHandler {
private val connections = mutable.Set.empty[WebSocketConnection]
override def onConnect(connection: WebSocketConnection): Unit = {
connections.synchronized {
connections += connection
}
broadcast(s"User joined. Total users: ${connections.size}")
}
override def onMessage(connection: WebSocketConnection, message: String): Unit = {
broadcast(s"User: $message")
}
override def onClose(
connection: WebSocketConnection,
statusCode: Option[Int],
reason: String
): Unit = {
connections.synchronized {
connections -= connection
}
broadcast(s"User left. Total users: ${connections.size}")
}
override def onError(connection: WebSocketConnection, error: Throwable): Unit = {
println(s"WebSocket error: ${error.getMessage}")
}
private def broadcast(message: String): Unit = {
connections.synchronized {
connections.foreach { conn =>
if (conn.isOpen) {
conn.sendText(message)
}
}
}
}
}
WebSocket Protocol Details
Spider's WebSocket implementation:
- Follows RFC 6455 (WebSocket Protocol)
- Supports text and binary messages
- Handles ping/pong frames automatically
- Supports message fragmentation
- Validates handshake headers
- Manages connection lifecycle (open, closing, closed)
Client Example
Here's an example JavaScript client for testing:
const ws = new WebSocket('ws://localhost:8080/ws/echo');
ws.onopen = () => {
console.log('Connected');
ws.send('Hello, server!');
};
ws.onmessage = (event) => {
console.log('Received:', event.data);
};
ws.onclose = (event) => {
console.log('Disconnected:', event.code, event.reason);
};
ws.onerror = (error) => {
console.error('Error:', error);
};
Next Steps
- Build HTTP applications with the HTTP Server
- Create reactive UIs with WebView which uses WebSockets internally
- Make HTTP requests with the HTTP Client