The File-Handler plugin serves static files and handles file operations including GET, PUT, POST, DELETE, and HEAD requests. It provides the core file serving functionality for Rusty Beam with security features and content type detection.
The file-handler plugin is the primary content-serving plugin in Rusty Beam. It maps URL paths to filesystem paths, serves files with appropriate content types, and supports file uploads and modifications. The plugin includes security features like path traversal protection and respects host-specific document roots.
The file-handler plugin is configured as part of the plugin pipeline in your host configuration:
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/file-handler.so</span>
<meta itemprop="root_dir" content="./public">
</li>
Parameter | Type | Required | Default | Description |
---|---|---|---|---|
root_dir |
String | No | "." | Default document root directory. Can be overridden by host-specific hostRoot |
hostRoot
configuration takes precedence over the plugin's root_dir
setting.
Serves files from the document root:
# Get a specific file
curl http://localhost:3000/index.html
# Get directory (serves index.html if exists)
curl http://localhost:3000/
# Get file from subdirectory
curl http://localhost:3000/assets/style.css
Creates new files or replaces existing ones:
# Create a new file
curl -X PUT http://localhost:3000/new-file.txt \
-H "Content-Type: text/plain" \
-d "File content here"
# Replace existing file
curl -X PUT http://localhost:3000/existing.html \
-H "Content-Type: text/html" \
-d "<html><body>Updated content</body></html>"
# Upload binary file
curl -X PUT http://localhost:3000/image.png \
-H "Content-Type: image/png" \
--data-binary @local-image.png
Response codes:
201 Created
- New file created200 OK
- Existing file updatedAppends content to existing files or creates new ones:
# Append to log file
curl -X POST http://localhost:3000/app.log \
-H "Content-Type: text/plain" \
-d "New log entry"
# Create file if doesn't exist
curl -X POST http://localhost:3000/notes.txt \
-d "First note"
Deletes files from the filesystem:
# Delete a file
curl -X DELETE http://localhost:3000/old-file.txt
# Response: 200 OK with "File deleted successfully"
# or 404 Not Found if file doesn't exist
Returns headers without body content:
# Check if file exists and get metadata
curl -I http://localhost:3000/document.pdf
# Response includes:
# Content-Type: application/pdf
# Content-Length: 1048576
Returns supported HTTP methods:
curl -X OPTIONS http://localhost:3000/
# Response header: Allow: GET, PUT, DELETE, OPTIONS, POST, HEAD
The plugin automatically sets Content-Type based on file extensions:
Extension | Content-Type |
---|---|
.html | text/html |
.css | text/css |
.js | application/javascript |
.json | application/json |
.txt | text/plain |
.png | image/png |
.jpg, .jpeg | image/jpeg |
.gif | image/gif |
other | application/octet-stream |
The plugin prevents directory traversal attacks by:
# These attacks are blocked:
curl http://localhost:3000/../etc/passwd # 403 Forbidden
curl http://localhost:3000/../../sensitive.txt # 403 Forbidden
curl http://localhost:3000/./././../../../etc/ # 403 Forbidden
The file-handler plugin should typically be placed near the end of the pipeline:
1. basic-auth.so → Authentication
2. authorization.so → Access control
3. cors.so → CORS headers
4. selector-handler.so → Dynamic content
5. file-handler.so → Static files ✓
6. error-handler.so → Error pages
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/file-handler.so</span>
<meta itemprop="root_dir" content="./static">
</li>
<!-- Host 1 configuration -->
<tr itemscope itemtype="https://rustybeam.net/schema/HostConfig">
<td itemprop="hostName">example.com</td>
<td itemprop="hostRoot">./sites/example</td>
<td>
<ol>
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/file-handler.so</span>
</li>
</ol>
</td>
</tr>
<!-- Host 2 configuration -->
<tr itemscope itemtype="https://rustybeam.net/schema/HostConfig">
<td itemprop="hostName">api.example.com</td>
<td itemprop="hostRoot">./sites/api</td>
<td>
<ol>
<li itemprop="plugin" itemscope itemtype="https://rustybeam.net/schema/Plugin">
<span itemprop="library">file://./plugins/file-handler.so</span>
</li>
</ol>
</td>
</tr>
#!/bin/bash
# upload.sh - Upload files to Rusty Beam
FILE="$1"
REMOTE_PATH="$2"
if [ -z "$FILE" ] || [ -z "$REMOTE_PATH" ]; then
echo "Usage: ./upload.sh <local-file> <remote-path>"
exit 1
fi
# Detect content type
CONTENT_TYPE=$(file -b --mime-type "$FILE")
# Upload file
curl -X PUT "http://localhost:3000/$REMOTE_PATH" \
-H "Content-Type: $CONTENT_TYPE" \
--data-binary "@$FILE"
async function uploadFile(file, path) {
const response = await fetch(`http://localhost:3000/${path}`, {
method: 'PUT',
headers: {
'Content-Type': file.type
},
body: file
});
if (response.status === 201) {
console.log('File created successfully');
} else if (response.status === 200) {
console.log('File updated successfully');
} else {
console.error('Upload failed:', response.statusText);
}
}
// Usage with file input
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
uploadFile(file, `uploads/${file.name}`);
}
});
project/
├── config.html
├── plugins/
│ └── file-handler.so
└── public/ # Document root
├── index.html # Default page
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
└── uploads/ # Writable directory for uploads
Issue | Cause | Solution |
---|---|---|
404 for existing files | Wrong document root | Check hostRoot or root_dir configuration |
403 Forbidden | Path traversal attempt or permissions | Use paths within document root, check file permissions |
PUT/POST failing | Directory doesn't exist or no write permission | Create parent directories, check permissions |
Wrong content type | Unknown file extension | File gets application/octet-stream by default |
Method not allowed | Using unsupported HTTP method | Use GET, PUT, POST, DELETE, HEAD, or OPTIONS |
Run the server with -v
flag to see file operations:
./rusty-beam -v config.html
The selector-handler plugin can modify HTML files before the file-handler serves them. Place selector-handler before file-handler for this to work.
Use the authorization plugin to control access to files based on user roles and paths.
The compression plugin can compress files served by file-handler. Place it after file-handler in the pipeline.
Accept-Ranges: selector
header, but actual Range request processing is handled by the selector-handler plugin.