WebSocket Plugin

The WebSocket plugin enables real-time bidirectional communication between clients and the Rusty Beam server. It provides automatic subscription to document changes and broadcasts live updates when content is modified through the selector-handler plugin.

Overview

This plugin implements the WebSocket protocol (RFC 6455) and integrates with Rusty Beam's selector-based architecture to provide real-time updates. When a WebSocket connects to a document URL, it automatically subscribes to all changes for that document. When the document is modified using HTTP requests with CSS selectors, all WebSocket clients connected to that URL receive immediate notifications in the StreamItem format.

Key Features

Configuration

To add the WebSocket plugin to your pipeline:

<table itemscope itemtype="https://rustybeam.net/schema/Plugin">
    <tbody>
        <tr>
            <td>Library</td>
            <td itemprop="library">file://./plugins/librusty_beam_websocket.so</td>
        </tr>
    </tbody>
</table>

Configuration Parameters

The WebSocket plugin currently has no configuration parameters. All settings are handled automatically.

Parameter Type Required Default Description
No configuration parameters required

Plugin Pipeline Placement

Important: The WebSocket plugin should be placed early in the pipeline, before file-handler and selector-handler plugins. This ensures it can intercept WebSocket upgrade requests before other plugins process them.

Recommended pipeline order:

  1. access-log
  2. basic-auth (if needed)
  3. authorization (if needed)
  4. websocket
  5. selector-handler
  6. file-handler

Examples

WebSocket Connection with curl

# Test WebSocket upgrade
curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
  -H "Sec-WebSocket-Version: 13" \
  http://localhost:3000/document.html

# Expected response:
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

JavaScript WebSocket Client

// Connect to WebSocket for the current page
const ws = new WebSocket(`ws://localhost:3000${window.location.pathname}`);

ws.onopen = function() {
    console.log('WebSocket connected');
};

ws.onmessage = function(event) {
    // Parse the StreamItem
    const parser = new DOMParser();
    const doc = parser.parseFromString(event.data, 'text/html');
    const streamItem = doc.querySelector('[itemtype="https://rustybeam.net/schema/StreamItem"]');
    
    if (streamItem) {
        const method = streamItem.querySelector('[itemprop="method"]').textContent;
        const url = streamItem.querySelector('[itemprop="url"]').textContent;
        const selector = streamItem.querySelector('[itemprop="selector"]').textContent;
        const content = streamItem.querySelector('[itemprop="content"]').innerHTML;
        
        console.log(`${method} ${url} ${selector}:`, content);
        
        // Apply the update to the current page if it matches
        if (url === window.location.pathname) {
            const target = document.querySelector(selector);
            if (target) {
                target.outerHTML = content;
            }
        }
    }
};

Python WebSocket Client

import websocket
from bs4 import BeautifulSoup

def on_message(ws, message):
    print(f"Received: {message}")
    
    # Parse StreamItem
    soup = BeautifulSoup(message, 'html.parser')
    stream_item = soup.find(attrs={'itemtype': 'https://rustybeam.net/schema/StreamItem'})
    
    if stream_item:
        method = stream_item.find(attrs={'itemprop': 'method'}).text
        url = stream_item.find(attrs={'itemprop': 'url'}).text
        selector = stream_item.find(attrs={'itemprop': 'selector'}).text
        content = stream_item.find(attrs={'itemprop': 'content'}).decode_contents()
        
        print(f"{method} {url} {selector}: {content}")

def on_open(ws):
    print("WebSocket connected - automatically subscribed to document changes")

ws = websocket.WebSocketApp("ws://localhost:3000/blog/post.html",
                            on_open=on_open,
                            on_message=on_message)
ws.run_forever()

Triggering Updates

Updates are triggered when content is modified using the selector-handler plugin:

# This will broadcast to all WebSocket clients connected to /document.html
curl -X PUT \
  -H "Range: selector=#content" \
  -d '<div id="content">Updated content</div>' \
  http://localhost:3000/document.html

StreamItem Format

Updates are broadcast in the StreamItem microdata format:

<div itemscope itemtype="https://rustybeam.net/schema/StreamItem">
    <span itemprop="method">PUT</span>
    <span itemprop="url">/document.html</span>
    <span itemprop="selector">#content</span>
    <div itemprop="content">
        <div id="content">Updated content</div>
    </div>
</div>

Connection Protocol

The WebSocket plugin uses a simple connection-based subscription model:

  1. Connect: Connect to ws://host:port/document.html
  2. Automatic Subscription: The connection automatically subscribes to all changes for /document.html
  3. Receive Updates: Receive StreamItem messages when the document is modified

No explicit subscription messages are required - the subscription is based on the URL path of the WebSocket connection.

Integration with Other Plugins

Selector-Handler Plugin

The WebSocket plugin relies on metadata set by the selector-handler plugin. When selector-handler successfully applies a change, it sets:

Authorization Plugin

WebSocket connections respect the same authorization rules as HTTP requests. Clients must be authorized to access the document URL they're connecting to.

Access-Log Plugin

WebSocket upgrade requests are logged like any other HTTP request. The connection upgrade shows as a 101 status code in access logs.

Troubleshooting

Issue Possible Cause Solution
Connection fails with 404 WebSocket plugin not in pipeline Ensure WebSocket plugin is loaded before file-handler
No updates received Connected to wrong URL or no updates occurring Verify WebSocket connects to the same URL being updated
Connection closes immediately Authorization failure Check authorization rules for the document URL
Invalid upgrade response Missing WebSocket headers Ensure client sends all required WebSocket headers

Debug Logging

Enable verbose mode to see WebSocket activity:

./rusty-beam -v config.html

This will show:

Security Considerations

Performance Notes

See Also