Skip to content

WebSocket SSE

github-actions[bot] edited this page Feb 9, 2026 · 4 revisions

WebSocket and Server-Sent Events

Elio provides full support for WebSocket (RFC 6455) and Server-Sent Events (SSE) for real-time bidirectional and unidirectional communication.

Design Notes

The WebSocket implementation follows RFC 6455, including frame masking for all client-to-server messages as required by the specification. Masking prevents cache poisoning attacks on intermediary proxies that might misinterpret WebSocket frames as HTTP. The server-side does not mask frames, as the RFC only mandates client-to-server masking.

Server-Sent Events (SSE) provides a simpler alternative to WebSocket for server-to-client streaming. SSE operates over standard HTTP, requires no protocol upgrade, and benefits from automatic reconnection built into the browser EventSource API. The tradeoff is that SSE is unidirectional (server to client only), but for many use cases -- notifications, live feeds, progress updates -- this is sufficient and avoids the complexity of a bidirectional protocol.

WebSocket

WebSocket provides full-duplex communication channels over a single TCP connection.

WebSocket Server

#include <elio/elio.hpp>
#include <elio/http/websocket.hpp>

using namespace elio;
using namespace elio::http::websocket;

// WebSocket message handler
coro::task<void> echo_handler(ws_connection& conn) {
    while (conn.is_open()) {
        auto msg = co_await conn.receive();
        if (!msg) break;
        
        // Echo back the message
        if (msg->type == opcode::text) {
            co_await conn.send_text(msg->data);
        } else if (msg->type == opcode::binary) {
            co_await conn.send_binary(msg->data);
        }
    }
}

int main() {
    // Create WebSocket-enabled router
    ws_router router;
    
    // Regular HTTP routes
    router.get("/", [](context& ctx) -> coro::task<response> {
        co_return response::ok("Hello!");
    });
    
    // WebSocket routes
    server_config ws_config;
    ws_config.max_message_size = 1024 * 1024;  // 1MB
    
    router.websocket("/ws", echo_handler, ws_config);
    
    // Create and start server
    ws_server srv(std::move(router));
    
    runtime::scheduler sched(4);
    sched.start();
    
    auto task = srv.listen(net::ipv4_address(8080));
    sched.spawn(task.release());
    
    // Run until stopped...
    sched.shutdown();
    return 0;
}

WebSocket Client

#include <elio/elio.hpp>
#include <elio/http/websocket.hpp>

using namespace elio;
using namespace elio::http::websocket;

coro::task<void> connect_example() {
    // Create client
    client_config config;
    config.subprotocols = {"chat", "json"};  // Optional subprotocols

    ws_client client(config);
    
    // Connect to server
    if (!co_await client.connect("ws://localhost:8080/ws")) {
        ELIO_LOG_ERROR("Failed to connect");
        co_return;
    }
    
    ELIO_LOG_INFO("Connected! Subprotocol: {}", client.subprotocol());
    
    // Send messages
    co_await client.send_text("Hello, server!");
    co_await client.send_binary("\x01\x02\x03");
    
    // Receive messages
    while (client.is_open()) {
        auto msg = co_await client.receive();
        if (!msg) break;
        
        if (msg->type == opcode::text) {
            ELIO_LOG_INFO("Received: {}", msg->data);
        }
    }
    
    // Close connection
    co_await client.close(close_code::normal, "Done");
}

WebSocket Frame Types

Opcode Name Description
0x0 continuation Continuation of fragmented message
0x1 text UTF-8 text data
0x2 binary Binary data
0x8 close Connection close
0x9 ping Ping (keep-alive)
0xA pong Pong (response to ping)

Close Codes

Code Name Description
1000 normal Normal closure
1001 going_away Endpoint going away
1002 protocol_error Protocol error
1003 unsupported Unsupported data type
1008 policy_violation Policy violation
1009 too_large Message too big
1011 unexpected Unexpected condition

Secure WebSocket (WSS)

For secure connections, use wss:// URLs and configure TLS:

// Server with TLS
auto tls_ctx = tls::tls_context::make_server("cert.pem", "key.pem");
auto task = srv.listen_tls(net::ipv4_address(8443), 
                          io::default_io_context(), sched, tls_ctx);

// Client with TLS
ws_client client(ctx);
client.tls_context().use_default_verify_paths();
co_await client.connect("wss://example.com/ws");

Server-Sent Events (SSE)

SSE provides server-to-client event streaming over HTTP.

SSE Server

#include <elio/elio.hpp>
#include <elio/http/sse.hpp>

using namespace elio;
using namespace elio::http::sse;

// Event stream handler
coro::task<void> event_stream(sse_connection& conn) {
    int counter = 0;
    
    // Send retry interval
    co_await conn.send_retry(3000);  // 3 seconds
    
    while (conn.is_active()) {
        ++counter;
        
        // Send a simple event
        co_await conn.send_data("Counter: " + std::to_string(counter));
        
        // Send a typed event
        event evt = event::full(
            std::to_string(counter),  // id
            "update",                  // type
            R"({"count":)" + std::to_string(counter) + "}"
        );
        co_await conn.send(evt);
        
        // Wait before next event
        co_await time::sleep_for(std::chrono::seconds(1));
    }
}

SSE Client

#include <elio/elio.hpp>
#include <elio/http/sse.hpp>

using namespace elio;
using namespace elio::http::sse;

coro::task<void> listen_events() {
    auto& ctx = io::default_io_context();
    
    // Configure client
    client_config config;
    config.auto_reconnect = true;
    config.default_retry_ms = 3000;
    
    sse_client client(ctx, config);
    
    // Connect
    if (!co_await client.connect("http://localhost:8080/events")) {
        ELIO_LOG_ERROR("Failed to connect");
        co_return;
    }
    
    // Receive events
    while (client.is_connected()) {
        auto evt = co_await client.receive();
        if (!evt) break;
        
        ELIO_LOG_INFO("Event: type={} id={} data={}", 
                      evt->type.empty() ? "message" : evt->type,
                      evt->id,
                      evt->data);
    }
}

SSE Event Format

SSE events are formatted as text with specific fields:

id: event-123
event: notification
retry: 3000
data: Hello, World!
data: This is line 2

  • id: - Event ID for reconnection tracking
  • event: - Event type (default: "message")
  • retry: - Reconnection interval in milliseconds
  • data: - Event data (can span multiple lines)
  • Empty line marks end of event

SSE vs WebSocket

Feature SSE WebSocket
Direction Server → Client only Bidirectional
Protocol HTTP Custom over TCP
Reconnection Automatic Manual
Browser support EventSource API WebSocket API
Complexity Simple More complex
Best for Notifications, feeds Chat, games, real-time

When to Use SSE

  • Real-time notifications
  • Live feeds (news, stocks)
  • Progress updates
  • Log streaming
  • Any server-push scenario without client messages

When to Use WebSocket

  • Chat applications
  • Real-time games
  • Collaborative editing
  • Any scenario requiring bidirectional communication

Browser Integration

WebSocket (JavaScript)

const ws = new WebSocket('ws://localhost:8080/ws');

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);
};

// Send messages
ws.send('Hello');
ws.send(new Blob([1, 2, 3]));

// Close
ws.close(1000, 'Done');

SSE (JavaScript)

const es = new EventSource('/events');

es.onmessage = (event) => {
    console.log('Message:', event.data);
};

es.addEventListener('notification', (event) => {
    console.log('Notification:', event.data);
    console.log('ID:', event.lastEventId);
});

es.onerror = (error) => {
    console.error('Error:', error);
    // EventSource will auto-reconnect
};

// Close
es.close();

Examples

Complete examples are available:

  • examples/websocket_server.cpp - WebSocket server with echo and chat
  • examples/websocket_client.cpp - WebSocket client demo
  • examples/sse_server.cpp - SSE event streaming server
  • examples/sse_client.cpp - SSE event receiver

Next Steps

Clone this wiki locally