A modular, plugin-based HTTP/1.1 web server written in Rust. This server acts as a lightweight core that delegates request handling to external plugins, allowing for flexible and extensible web application development.
This web server is designed with a minimal core that focuses on request routing and plugin management. The actual request processing logic is implemented in separate plugin applications that communicate with the core server through a well-defined protocol. This architecture provides several benefits:
The server consists of the following main components:
For a detailed description of all components, see architecture/module description.md.
cargo build --release
cargo run --release -- --port 8080
Command-line options:
-p, --port <PORT>: Port to bind the server to (default: 80)The server expects the following directory structure:
rust_webserver/
├── plugins/ # Plugin directory (scanned recursively)
│ ├── plugin1/
│ │ ├── pluginConfig.json
│ │ └── plugin_executable
│ └── plugin2/
│ ├── pluginConfig.json
│ └── plugin_executable
├── error_logs/ # Error logs from plugins
└── websites/ # Optional: static file hosting (plugin-dependent)
The server will automatically create the plugins/ and error_logs/ directories if they don't exist.
Each plugin must have a pluginConfig.json file that describes its behavior and requirements.
{
"pluginName": "MyPlugin",
"startupCommand": "./my_plugin_executable",
"protocol": "STD_IO_JSON",
"maxRequestTimeout": 5000,
"maxStartupTime": 5000,
"requestInformation": {
"requestMethods": ["GET", "POST"],
"hosts": ["example.com", "*.example.com"],
"paths": ["/api/*", "/users/:id"]
}
}
STD_IO_JSON["GET", "POST"])["*"], ["*.example.com"])["/api/*"], ["/users/:id"])When multiple plugins match a request, the server uses a specificity algorithm to select the most appropriate plugin:
example.com > *.example.com > *)/api/users > /api/* > /*)Plugins communicate with the server core through stdin/stdout using a binary-framed JSON protocol. The protocol is fully documented in architecture/communication protocol.md.
All packages are sent with a 4-byte length prefix followed by the JSON payload:
[4 bytes: length] [JSON payload]
For detailed protocol specifications, package formats, and examples, please refer to the communication protocol documentation.
# Read package from stdin
def read_package():
length_bytes = stdin.read(4)
length = int.from_bytes(length_bytes, 'big')
json_data = stdin.read(length)
return json.loads(json_data)
# Send package to stdout
def send_package(package):
json_data = json.dumps(package)
length = len(json_data)
stdout.write(length.to_bytes(4, 'big'))
stdout.write(json_data.encode())
stdout.flush()
# Main loop
while True:
package = read_package()
if package['packageType'] == 'handshakeRequest':
send_package({
'packageType': 'handshakeResponse',
'content': {
'responseCode': 0,
'responseCodeText': 'Success'
}
})
elif package['packageType'] == 'normalRequest':
http_request = package['content']
# Process the HTTP request
response = process_request(http_request)
send_package({
'packageType': 'normalResponse',
'content': response
})
The repository includes example plugins:
Refer to the plugins/ directory for complete implementations.
cargo test
RUST_LOG=debug cargo run -- --port 8080
src/
├── main.rs # Server entry point
├── lib.rs # Library exports
├── webserver/ # HTTP server implementation
│ ├── webserver.rs # Webserver trait
│ └── http_1_server.rs # HTTP/1.1 implementation
├── plugin/ # Plugin management
│ ├── plugin_manager.rs # Plugin lifecycle and routing
│ ├── plugin_entry.rs # Plugin configuration
│ ├── plugin_config.rs # Config parsing
│ └── running_plugin.rs # Active plugin instance
├── plugin_communication/ # Plugin communication layer
│ ├── plugin_communicator.rs # Communication interface
│ ├── package_handler.rs # Binary protocol handler
│ ├── models.rs # Data structures
│ ├── protocols/ # Protocol implementations
│ └── app_starter/ # Process management
│ ├── plugin_starter.rs # Plugin launcher interface
│ ├── default_plugin_starter.rs # Default launcher
│ └── default_program_controller.rs # Process controller
└── io/ # File system abstraction
├── data_storage.rs # Storage interface
└── in_memory_storage.rs # Test implementation
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit issues or pull requests.
Q: Can plugins be written in languages other than Rust?
A: Yes! Plugins can be written in any language that supports reading from stdin and writing to stdout.
Q: How do I debug a plugin?
A: You can use the test binaries in src/bin/ to test plugins in isolation. Set RUST_LOG=debug for detailed logging.
Q: What happens if a plugin crashes?
A: The server detects crashed plugins and logs errors to the error_logs/ directory. The plugin can be restarted manually or automatically (future feature).
Q: Can multiple plugins handle the same URL?
A: Yes, but the server will route the request to the plugin with the most specific matching pattern.
Q: How do I update a plugin without restarting the server?
A: Currently, you need to manually restart the server. Hot reloading is planned for a future release.