Selector-Handler Plugin

The Selector-Handler plugin enables CSS selector-based HTML manipulation through HTTP Range headers. This unique feature allows clients to request specific HTML elements from documents, enabling fine-grained content extraction and modification.

Overview

Rusty Beam intentionally repurposes the HTTP Range header to support CSS selectors, providing a powerful API for HTML manipulation. The selector-handler plugin processes these special Range headers and returns only the selected HTML elements.

Design Note: The use of Range headers for CSS selectors is an intentional design feature of Rusty Beam, not a misuse of HTTP standards. This creative approach enables powerful HTML manipulation capabilities while maintaining compatibility with standard HTTP infrastructure.

Key Features

Configuration

The selector-handler plugin requires minimal configuration:

<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
    <span itemprop="library">file://./plugins/selector-handler.so</span>
</li>

Configuration Parameters

The selector-handler plugin currently has no configuration parameters. It processes all requests with Range: selector= headers.

Range Header Format

The plugin recognizes Range headers in the following format:

Range: selector={css-selector}

Examples

URL Encoding

Complex selectors should be URL-encoded in the Range header:

# Selector: div > p.highlight
Range: selector=div%20%3E%20p.highlight

# Selector: a[href^="https://"]
Range: selector=a%5Bhref%5E%3D%22https%3A%2F%2F%22%5D

Request Methods

GET Requests

Retrieve specific elements from an HTML document:

GET /page.html HTTP/1.1
Host: localhost:3000
Range: selector=h1,h2

# Response: HTML fragment containing all h1 and h2 elements

PUT Requests

Replace selected elements with new content:

PUT /page.html HTTP/1.1
Host: localhost:3000
Range: selector=.announcement
Content-Type: text/html

<div class="announcement">Updated announcement content</div>

POST Requests

Append content to selected elements:

POST /guestbook.html HTTP/1.1
Host: localhost:3000
Range: selector=ul.entries
Content-Type: text/html

<li>New guestbook entry</li>

DELETE Requests

Remove selected elements from the document:

DELETE /page.html HTTP/1.1
Host: localhost:3000
Range: selector=.deprecated-content

Response Format

Successful Selection (206 Partial Content)

When elements are found, the plugin returns HTTP 206:

HTTP/1.1 206 Partial Content
Content-Type: text/html

<h1>Page Title</h1>
<h1>Section Title</h1>

No Matches (416 Range Not Satisfiable)

When no elements match the selector:

HTTP/1.1 416 Range Not Satisfiable
Content-Type: text/plain

No elements match selector: .nonexistent

Invalid Selector (400 Bad Request)

When the CSS selector is malformed:

HTTP/1.1 400 Bad Request
Content-Type: text/plain

Invalid CSS selector: div >> p

CSS Selector Support

The plugin supports standard CSS3 selectors:

Basic Selectors

Selector Description Example
element Type selector p, div, span
.class Class selector .highlight, .nav-item
#id ID selector #header, #main-content
* Universal selector *, div *

Combinators

Combinator Description Example
A B Descendant article p
A > B Child ul > li
A + B Adjacent sibling h1 + p
A ~ B General sibling h1 ~ p

Attribute Selectors

Selector Description Example
[attr] Has attribute [disabled]
[attr=value] Exact match [type="text"]
[attr^=value] Starts with [href^="https://"]
[attr$=value] Ends with [src$=".png"]
[attr*=value] Contains [class*="nav"]

Pseudo-classes

Common pseudo-classes supported:

Plugin Pipeline Placement

Important: The selector-handler plugin should be placed:
  1. After authentication plugins (basic-auth)
  2. After authorization plugins (for element-level access control)
  3. Before the file-handler plugin

Typical pipeline order:

1. basic-auth.so      → Authenticates user
2. authorization.so   → Checks selector permissions
3. selector-handler.so → Processes selector requests
4. file-handler.so    → Serves complete files
5. access-log.so      → Logs requests

Authorization Integration

The selector-handler plugin works with the authorization plugin to enable element-level access control:

<!-- Authorization rule example -->
<tr itemscope itemtype="https://rustybeam.net/schema/AuthorizationRule">
    <td itemprop="username">*</td>
    <td itemprop="path">/page.html</td>
    <td itemprop="selector">.public</td>
    <td itemprop="method">GET</td>
    <td itemprop="action">allow</td>
</tr>

This allows anonymous users to access only elements with class "public" from page.html.

Use Cases

Content Extraction

Extract specific content from web pages:

# Get all article headlines
curl -H "Range: selector=article h2" http://localhost:3000/blog.html

# Extract navigation menu
curl -H "Range: selector=nav ul" http://localhost:3000/index.html

Dynamic Content Updates

Update specific page sections without modifying the entire document:

# Update news section
curl -X PUT -H "Range: selector=#news" \
     -H "Content-Type: text/html" \
     -d '<div id="news">Latest news here</div>' \
     http://localhost:3000/index.html

Form Handling

Append entries to lists:

# Add comment to comment list
curl -X POST -H "Range: selector=ul#comments" \
     -H "Content-Type: text/html" \
     -d '<li>New comment</li>' \
     http://localhost:3000/comments.html

Content Filtering

Implement access control at the element level:

Performance Considerations

Limitations

Error Handling

Error HTTP Status Description
Invalid selector syntax 400 Bad Request CSS selector is malformed
No matching elements 416 Range Not Satisfiable Selector matches no elements
File not found 404 Not Found Requested file doesn't exist
Not an HTML file Pass through Non-HTML files passed to next plugin
Authorization denied 403 Forbidden User lacks permission for selector

Examples

Command Line (curl)

# Get all paragraphs
curl -H "Range: selector=p" http://localhost:3000/article.html

# Get element by ID
curl -H "Range: selector=#main-content" http://localhost:3000/page.html

# Update footer
curl -X PUT -H "Range: selector=footer" \
     -H "Content-Type: text/html" \
     -d '<footer>© 2024 Updated</footer>' \
     http://localhost:3000/page.html

# Delete advertisements
curl -X DELETE -H "Range: selector=.advertisement" \
     http://localhost:3000/page.html

JavaScript

// Fetch specific elements
fetch('http://localhost:3000/page.html', {
    headers: {
        'Range': 'selector=article h2'
    }
})
.then(response => response.text())
.then(html => {
    console.log('Headlines:', html);
});

// Update element
fetch('http://localhost:3000/page.html', {
    method: 'PUT',
    headers: {
        'Range': 'selector=#status',
        'Content-Type': 'text/html'
    },
    body: '<div id="status">Online</div>'
});

Python

import requests

# Get navigation menu
response = requests.get(
    'http://localhost:3000/index.html',
    headers={'Range': 'selector=nav'}
)
print(response.text)

# Append to list
response = requests.post(
    'http://localhost:3000/tasks.html',
    headers={
        'Range': 'selector=ul#task-list',
        'Content-Type': 'text/html'
    },
    data='<li>New task</li>'
)

Troubleshooting

Debug Logging

Enable verbose logging to see selector processing:

./rusty-beam -v config.html

Common Issues

Issue Cause Solution
Always returns full document Plugin not loaded or wrong order Check plugin configuration and order
416 for valid selectors Selector matches no elements Verify selector and HTML structure
403 Forbidden Authorization rules blocking Check authorization configuration
Encoding issues Special characters in selector URL-encode the selector value

See Also