Introduction
BWS is a high-performance, multi-site web server built with Pingora, Cloudflare's battle-tested proxy framework.
What is BWS?
BWS hosts multiple websites on different ports with individual configurations. It combines Pingora's reliability and performance with comprehensive error handling, automatic SSL management, and production-grade features.
Key Features
- Multi-Site Support: Host multiple websites with individual configurations
- Reverse Proxy & Load Balancing: Full proxy functionality with multiple algorithms
- SSL/TLS Support: Automatic and manual certificate management
- High Performance: Built on Pingora for enterprise-grade reliability
- Security Focused: Zero-panic policy with comprehensive error handling
- Container Ready: Docker support with multi-architecture images
- Hot Reload: Zero-downtime configuration updates
Use Cases
- Development environments: Quick multi-site testing
- Static site hosting: Efficient multi-website serving
- Reverse proxy: Load balancing to backend services
- Microservices: Service routing and load balancing
- API gateways: Centralized API entry point
Architecture
Built on Cloudflare's Pingora framework:
- Battle-tested reliability (handles millions of requests)
- High performance with low latency
- Memory safety with Rust
- Modern networking (HTTP/2, HTTP/3 ready)
Getting Started
Start with the Installation guide or jump to Quick Start for immediate setup.
Installation
BWS can be installed in several ways. Choose the method that best fits your needs.
From crates.io (Recommended for Development)
The easiest way to install BWS is using Cargo:
cargo install bws-web-server
This will install the latest stable version from crates.io. The package name is bws-web-server, but the installed binary is named bws.
Prerequisites
- Rust: Version 1.89.0 or later
- Cargo: Comes with Rust installation
From Docker (Recommended for Production)
Docker is the recommended deployment method for production environments:
# Pull and run the latest version
docker run -d -p 8080:8080 ghcr.io/benliao/bws:latest
# Or with custom configuration
docker run -d \
-p 8080:8080 \
-v $(pwd)/config.toml:/app/config.toml:ro \
-v $(pwd)/static:/app/static:ro \
ghcr.io/benliao/bws:latest
# Validate configuration before running
docker run --rm \
-v $(pwd)/config.toml:/app/config.toml:ro \
ghcr.io/benliao/bws:latest \
bws --config config.toml --dry-run
Available Docker Tags
latest- Latest stable releasev0.3.4- Specific version (recommended for production)main- Latest development build
From Source
For development or custom builds:
# Clone the repository
git clone https://github.com/benliao/bws.git
cd bws
# Validate the build configuration
cargo check
# Build in debug mode
cargo build --bin bws
# Build optimized release
cargo build --release --bin bws
# Test the binary
./target/release/bws --version
./target/release/bws --help
# Validate example configurations
./target/release/bws --config examples/basic-single-site.toml --dry-run
# The binary will be in target/release/bws
Testing Your Build
After building from source, run the test suite:
# Run unit tests
cargo test
# Run configuration validation tests
./tests/scripts/validate-configs.sh
# Run integration tests (if server dependencies available)
./tests/scripts/test_headers.sh
Pre-built Binaries
Download pre-built binaries from GitHub Releases:
Linux (x86_64)
wget https://github.com/benliao/bws/releases/latest/download/bws-linux-x86_64.tar.gz
tar -xzf bws-linux-x86_64.tar.gz
chmod +x bws
macOS (x86_64)
wget https://github.com/benliao/bws/releases/latest/download/bws-macos-x86_64.tar.gz
tar -xzf bws-macos-x86_64.tar.gz
chmod +x bws
macOS (ARM64/Apple Silicon)
wget https://github.com/benliao/bws/releases/latest/download/bws-macos-aarch64.tar.gz
tar -xzf bws-macos-aarch64.tar.gz
chmod +x bws
Windows (x86_64)
wget https://github.com/benliao/bws/releases/latest/download/bws-windows-x86_64.exe
# Note: Windows executable is directly usable
Verification
After installation, verify BWS is working:
# Check version
bws --version
# Display help
bws --help
Next Steps
Once installed, proceed to the Quick Start guide to set up your first BWS server!
Quick Start
Get BWS running in minutes with these simple options.
Option 1: Instant Directory Server
Serve files immediately without configuration:
# Serve current directory on port 80
bws .
# Serve specific directory on custom port
bws /path/to/website --port 8080
# Validate setup before starting
bws /path/to/website --port 8080 --dry-run
Example:
mkdir my-website
echo "<h1>Welcome to BWS!</h1>" > my-website/index.html
bws my-website --port 8080
Option 2: Configuration File Setup
1. Create Configuration
Create config.toml:
[server]
name = "BWS Server"
# HTTP site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
[sites.headers]
"X-Powered-By" = "BWS"
# HTTPS site with auto SSL
[[sites]]
name = "secure"
hostname = "secure.localhost"
port = 8443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.localhost"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = true
challenge_dir = "./acme-challenges"
2. Create Content
mkdir -p static acme-challenges
cat > static/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>BWS Server</title></head>
<body>
<h1>๐ BWS is Running!</h1>
<ul>
<li><a href="http://localhost:8080">HTTP Site</a></li>
<li><a href="https://secure.localhost:8443">HTTPS Site</a></li>
<li><a href="/api/health">Health Check</a></li>
</ul>
</body>
</html>
EOF
3. Start Server
# Validate first
bws --config config.toml --dry-run
# Start server
bws --config config.toml
Test Your Setup
# Test HTTP
curl http://localhost:8080/
# Health check
curl http://localhost:8080/api/health
# Test HTTPS (add to /etc/hosts if needed)
curl -k https://secure.localhost:8443/
Management API
BWS includes an optional secure management API for configuration reloads. To enable it, add to your config:
[management]
enabled = true
host = "127.0.0.1"
port = 7654
api_key = "your-secure-key" # Optional but recommended
Usage:
# Hot reload configuration
curl -X POST http://127.0.0.1:7654/api/config/reload
Command Options
# Common options
bws --config config.toml # Use config file
bws --verbose # Enable verbose logging
bws --daemon # Run as daemon (Unix only)
bws --dry-run # Validate config only
Next Steps
- Configuration - Detailed configuration options
- SSL/TLS Setup - HTTPS configuration
- Multi-Site - Host multiple websites
- Docker - Container deployment
Configuration
BWS uses TOML configuration files to define server and site settings.
Basic Structure
[server]
name = "BWS Server"
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
Configuration Validation
Always validate your configuration before starting:
bws --config config.toml --dry-run
Server Configuration
Global server settings:
[server]
name = "BWS Production Server" # Server name
Sites Configuration
Define websites using [[sites]] arrays:
Required Fields
[[sites]]
name = "main" # Unique site identifier
hostname = "example.com" # Domain name
port = 443 # Port number
static_dir = "static" # Static files directory
Optional Fields
default = true # Default site (catches all requests)
api_only = false # API-only mode (no static files)
hostnames = ["www.example.com"] # Additional hostnames
SSL/TLS Configuration
Automatic SSL (Let's Encrypt)
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false # Use false for production
challenge_dir = "./acme-challenges"
Manual SSL
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/example.com.crt"
key_file = "./certs/example.com.key"
Custom Headers
[sites.headers]
"X-Powered-By" = "BWS"
"Cache-Control" = "public, max-age=3600"
"Strict-Transport-Security" = "max-age=31536000"
Reverse Proxy
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "backend"
url = "http://127.0.0.1:3001"
weight = 1
[[sites.proxy.routes]]
path = "/api/"
upstream = "backend"
strip_prefix = false
websocket = false
[sites.proxy.load_balancing]
method = "round_robin" # round_robin, weighted, least_connections
Management API
[management]
enabled = true # Disabled by default - must enable explicitly
host = "127.0.0.1" # Always localhost for security
port = 7654
api_key = "your-secure-key" # Optional but recommended
Usage:
# Reload configuration
curl -X POST http://127.0.0.1:7654/api/config/reload \
-H "X-API-Key: your-secure-key"
Performance Settings
[performance]
worker_threads = 8
max_connections = 1000
keep_alive_timeout = 60
request_timeout = 30
Security Settings
[security]
hide_server_header = true
max_request_size = "10MB"
[security.security_headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"
Logging
[logging]
level = "info" # debug, info, warn, error
format = "combined" # combined, compact, json
log_requests = true
Complete Example
[server]
name = "BWS Production Server"
# Main HTTPS site
[[sites]]
name = "main"
hostname = "example.com"
port = 443
static_dir = "static"
default = true
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000"
"X-Frame-Options" = "DENY"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
# API proxy site
[[sites]]
name = "api"
hostname = "api.example.com"
port = 443
static_dir = "static"
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "api-backend"
url = "http://127.0.0.1:3001"
[[sites.proxy.routes]]
path = "/v1/"
upstream = "api-backend"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["api.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
# Management API
[management]
enabled = true
port = 7654
api_key = "secure-random-key"
# Performance
[performance]
worker_threads = 8
max_connections = 1000
# Security
[security]
hide_server_header = true
[security.security_headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
# Logging
[logging]
level = "info"
log_requests = true
File Locations
BWS looks for configuration files in this order:
- File specified with
--configflag config.tomlin current directorybws.tomlin current directory
Environment Variables
Override configuration with environment variables:
BWS_CONFIG_FILE- Configuration file pathBWS_LOG_LEVEL- Logging level (debug, info, warn, error)BWS_WORKERS- Number of worker threads
Multi-Site Setup
BWS hosts multiple websites with individual configurations on different ports or hostnames.
Basic Multi-Site Configuration
[server]
name = "BWS Multi-Site Server"
# Main HTTP site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
[sites.headers]
"X-Site-Name" = "Main Website"
# HTTPS blog site
[[sites]]
name = "blog"
hostname = "blog.localhost"
port = 8443
static_dir = "static-blog"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["blog.localhost"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
# API proxy site
[[sites]]
name = "api"
hostname = "api.localhost"
port = 8080
static_dir = "static"
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "backend"
url = "http://127.0.0.1:3001"
[[sites.proxy.routes]]
path = "/v1/"
upstream = "backend"
Setup Multi-Site Content
Create directories and content for each site:
# Create directories
mkdir -p static static-blog acme-challenges
# Main site content
cat > static/index.html << 'EOF'
<h1>Main Website</h1>
<p>Welcome to the main site</p>
<a href="https://blog.localhost:8443">Visit Blog</a>
EOF
# Blog content
cat > static-blog/index.html << 'EOF'
<h1>Blog Site</h1>
<p>This is the blog running on HTTPS</p>
<a href="http://localhost:8080">Back to Main</a>
EOF
Virtual Hosting (Same Port)
Host multiple sites on the same port using hostname routing:
[server]
name = "Virtual Hosting Server"
# Main site
[[sites]]
name = "main"
hostname = "www.example.com"
port = 80
static_dir = "sites/main"
default = true
# Blog site (same port, different hostname)
[[sites]]
name = "blog"
hostname = "blog.example.com"
port = 80
static_dir = "sites/blog"
# API site (same port, different hostname)
[[sites]]
name = "api"
hostname = "api.example.com"
port = 80
static_dir = "sites/api"
Testing Virtual Hosting
Add entries to /etc/hosts for local testing:
# Add to /etc/hosts
echo "127.0.0.1 www.example.com blog.example.com api.example.com" | sudo tee -a /etc/hosts
Test with curl:
curl -H "Host: www.example.com" http://localhost/
curl -H "Host: blog.example.com" http://localhost/
curl -H "Host: api.example.com" http://localhost/
Multi-Port Configuration
Run different services on different ports:
# HTTP on port 80
[[sites]]
name = "web"
hostname = "example.com"
port = 80
static_dir = "web"
# HTTPS on port 443
[[sites]]
name = "secure"
hostname = "example.com"
port = 443
static_dir = "web"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
# API on port 8080
[[sites]]
name = "api"
hostname = "api.example.com"
port = 8080
static_dir = "api"
# Admin panel on port 9000
[[sites]]
name = "admin"
hostname = "admin.example.com"
port = 9000
static_dir = "admin"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["admin.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
Site-Specific Configuration
Each site can have unique settings:
Headers
# Different headers per site
[[sites]]
name = "api"
hostname = "api.example.com"
port = 80
static_dir = "api"
[sites.headers]
"Access-Control-Allow-Origin" = "*"
"X-API-Version" = "v1"
[[sites]]
name = "blog"
hostname = "blog.example.com"
port = 80
static_dir = "blog"
[sites.headers]
"X-Content-Type" = "blog"
"Cache-Control" = "public, max-age=3600"
Security Settings
# Secure admin site
[[sites]]
name = "admin"
hostname = "admin.example.com"
port = 443
static_dir = "admin"
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["admin.example.com"]
Start Multi-Site Server
# Validate configuration
bws --config multi-site.toml --dry-run
# Start server
bws --config multi-site.toml
Testing Multiple Sites
# Test different sites
curl http://localhost:8080/ # Main site
curl https://blog.localhost:8443/ # Blog site
curl http://api.localhost:8080/v1/health # API site
# Check site information
curl http://localhost:8080/api/sites
# Test virtual hosting
curl -H "Host: www.example.com" http://localhost/
curl -H "Host: blog.example.com" http://localhost/
Management
Hot Reload
Update configuration without stopping the server:
# Edit configuration file
nano multi-site.toml
# Reload configuration
curl -X POST http://127.0.0.1:7654/api/config/reload
Monitoring
# Check all sites health
curl http://localhost:8080/api/health
# Monitor logs for all sites
tail -f /var/log/bws.log | grep -E "(site:|error:|ssl:)"
Production Deployment
Directory Structure
/var/www/
โโโ sites/
โ โโโ main/
โ โโโ blog/
โ โโโ api/
โ โโโ admin/
โโโ certs/
โโโ acme-challenges/
โโโ config/
โโโ multi-site.toml
Service Configuration
[server]
name = "Production Multi-Site"
[[sites]]
name = "main"
hostname = "example.com"
port = 443
static_dir = "/var/www/sites/main"
default = true
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
challenge_dir = "/var/www/acme-challenges"
[[sites]]
name = "api"
hostname = "api.example.com"
port = 443
static_dir = "/var/www/sites/api"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["api.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
challenge_dir = "/var/www/acme-challenges"
Firewall Setup
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow management API (localhost only)
sudo ufw allow from 127.0.0.1 to any port 7654
SSL/TLS Configuration
BWS supports both automatic (Let's Encrypt) and manual SSL certificates with per-site configuration.
Automatic SSL (Let's Encrypt)
Basic Setup
[[sites]]
name = "secure"
hostname = "example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false # Use true for testing
challenge_dir = "./acme-challenges"
ACME Options
| Field | Description | Default |
|---|---|---|
email | Contact email for Let's Encrypt | Required |
staging | Use staging environment for testing | false |
challenge_dir | Directory for HTTP-01 challenges | "./acme-challenges" |
Challenge Setup
# Create challenge directory
mkdir -p ./acme-challenges
# BWS automatically serves challenges at:
# http://yourdomain.com/.well-known/acme-challenge/
Requirements:
- Domain must be publicly accessible on port 80
- DNS must point to your server
- Challenge directory must be readable
Manual SSL Certificates
Configuration
[[sites]]
name = "manual-ssl"
hostname = "secure.local"
port = 8443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/secure.local.crt"
key_file = "./certs/secure.local.key"
Generate Self-Signed Certificate
# Create certificates directory
mkdir -p certs
# Generate self-signed certificate
openssl req -x509 -newkey rsa:4096 \
-keyout certs/secure.local.key \
-out certs/secure.local.crt \
-days 365 -nodes \
-subj "/CN=secure.local"
# Set proper permissions
chmod 600 certs/secure.local.key
chmod 644 certs/secure.local.crt
Use Existing Certificates
# Copy your existing certificates
cp /path/to/your.crt certs/
cp /path/to/your.key certs/
# Update permissions
chmod 600 certs/your.key
chmod 644 certs/your.crt
Mixed HTTP/HTTPS Setup
Host different sites with different SSL configurations:
# HTTP site
[[sites]]
name = "http"
hostname = "example.com"
port = 80
static_dir = "static"
# HTTPS site with auto SSL
[[sites]]
name = "https-auto"
hostname = "secure.example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
# HTTPS site with manual SSL
[[sites]]
name = "https-manual"
hostname = "internal.example.com"
port = 8443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/internal.crt"
key_file = "./certs/internal.key"
SSL Security Headers
Add security headers for HTTPS sites:
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "DENY"
Certificate Renewal
Automatic Renewal
- Certificates are automatically renewed before expiration
- No manual intervention required
- Renewal status logged to server logs
Manual Renewal
# Check certificate expiration
openssl x509 -in certs/your.crt -text -noout | grep "Not After"
# Replace expired certificates
cp /path/to/new.crt certs/
cp /path/to/new.key certs/
# Reload BWS configuration
curl -X POST http://127.0.0.1:7654/api/config/reload
Testing SSL Configuration
Verify Certificate
# Check certificate details
openssl x509 -in certs/your.crt -text -noout
# Test SSL connection
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
# Check certificate chain
curl -I https://yourdomain.com/
SSL Labs Test
Use SSL Labs to test your SSL configuration for security issues.
Troubleshooting
Common Issues
ACME Challenge Fails:
- Verify domain points to your server
- Check port 80 is accessible
- Ensure challenge directory exists and is readable
Certificate Not Loading:
- Check file paths in configuration
- Verify file permissions (key file should be 600)
- Check BWS logs for error messages
Mixed Content Warnings:
- Ensure all resources use HTTPS URLs
- Add security headers to prevent mixed content
Debug Commands
# Validate SSL configuration
bws --config config.toml --dry-run
# Check file permissions
ls -la certs/
# Test certificate
curl -v https://yourdomain.com/
# Check server logs
tail -f /var/log/bws.log
Static File Serving
BWS provides efficient static file serving with automatic MIME type detection, caching headers, and subdirectory support. BWS offers two ways to serve static files: instant directory serving and configuration-based serving.
Instant Directory Serving
The fastest way to serve static files is using BWS's built-in directory serving mode:
# Serve current directory
bws .
# Serve specific directory on custom port
bws /path/to/website --port 8080
# Windows example
bws.exe C:\websites\mysite --port 8080
Features
- No Configuration Required: Just point to a directory
- Automatic Site Setup: Creates temporary configuration with sensible defaults
- Cross-Platform: Handles Windows and Unix paths correctly
- Default Index Files: Automatically serves
index.html,index.htm, ordefault.html - Clean Paths: User-friendly path display on all platforms
Example
# Create test directory
mkdir my-site
echo "<h1>Hello BWS!</h1>" > my-site/index.html
# Start serving
bws my-site --port 8080
Output:
๐ Creating temporary web server:
๐ Directory: /path/to/my-site
๐ Port: 8080
๐ URL: http://localhost:8080
๐ Temporary web server ready!
๐ BWS Temporary Directory Server
Configuration-Based Static Serving
For production deployments and advanced features, use configuration files.
How Configuration-Based Static File Serving Works
BWS serves files from the static_dir configured for each site:
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static" # Files served from this directory
Supported File Types
BWS automatically detects MIME types for common file formats:
Web Files
- HTML:
.html,.htmโtext/html - CSS:
.cssโtext/css - JavaScript:
.js,.mjsโapplication/javascript - JSON:
.jsonโapplication/json
Images
- PNG:
.pngโimage/png - JPEG:
.jpg,.jpegโimage/jpeg - GIF:
.gifโimage/gif - SVG:
.svgโimage/svg+xml - WebP:
.webpโimage/webp - AVIF:
.avifโimage/avif - ICO:
.icoโimage/x-icon
Fonts
- WOFF:
.woff,.woff2โfont/woff - TTF:
.ttfโfont/ttf - OTF:
.otfโfont/otf - EOT:
.eotโapplication/vnd.ms-fontobject
Media
- MP4:
.mp4โvideo/mp4 - WebM:
.webmโvideo/webm - MP3:
.mp3โaudio/mpeg - WAV:
.wavโaudio/wav - OGG:
.oggโaudio/ogg
Documents
- PDF:
.pdfโapplication/pdf - XML:
.xmlโapplication/xml - Text:
.txtโtext/plain - Markdown:
.mdโtext/markdown
Archives
- ZIP:
.zipโapplication/zip - Gzip:
.gzโapplication/gzip - Tar:
.tarโapplication/x-tar
Configuration
- TOML:
.tomlโapplication/toml - YAML:
.yaml,.ymlโapplication/x-yaml
WebAssembly
- WASM:
.wasmโapplication/wasm
URL Patterns
BWS handles several URL patterns for static files:
Direct File Access
http://localhost:8080/index.html
http://localhost:8080/styles.css
http://localhost:8080/script.js
Static Directory Prefix
http://localhost:8080/static/css/main.css
http://localhost:8080/static/js/app.js
http://localhost:8080/static/images/logo.png
Subdirectory Support
http://localhost:8080/assets/css/main.css
http://localhost:8080/docs/api.html
http://localhost:8080/images/gallery/photo1.jpg
Index File Handling
BWS automatically serves index.html for directory requests:
http://localhost:8080/ โ static/index.html
http://localhost:8080/docs/ โ static/docs/index.html
http://localhost:8080/blog/ โ static/blog/index.html
Directory Structure Examples
Basic Website
static/
โโโ index.html # Main page
โโโ about.html # About page
โโโ styles.css # Stylesheet
โโโ script.js # JavaScript
โโโ favicon.ico # Site icon
Advanced Structure
static/
โโโ index.html
โโโ assets/
โ โโโ css/
โ โ โโโ main.css
โ โ โโโ theme.css
โ โ โโโ responsive.css
โ โโโ js/
โ โ โโโ app.js
โ โ โโโ utils.js
โ โ โโโ components/
โ โ โโโ header.js
โ โ โโโ footer.js
โ โโโ images/
โ โ โโโ logo.svg
โ โ โโโ hero.webp
โ โ โโโ gallery/
โ โ โโโ photo1.jpg
โ โ โโโ photo2.jpg
โ โโโ fonts/
โ โโโ Inter-Regular.woff2
โ โโโ Inter-Bold.woff2
โโโ docs/
โ โโโ index.html
โ โโโ api.html
โ โโโ guide.html
โโโ downloads/
โโโ manual.pdf
โโโ software.zip
Caching and Performance
BWS automatically adds caching headers:
Cache-Control: public, max-age=3600
Content-Type: text/css
Content-Length: 1234
Cache Control
- Static files: 1 hour cache by default
- HTML files: Shorter cache for dynamic content
- Assets: Longer cache for images, fonts, etc.
Security Features
Path Traversal Protection
BWS prevents directory traversal attacks:
http://localhost:8080/../../../etc/passwd โ Blocked
http://localhost:8080/..%2F..%2Fetc%2Fpasswd โ Blocked
File Type Restrictions
Only serves files from the configured static_dir:
http://localhost:8080/config.toml โ Not in static_dir
http://localhost:8080/.env โ Hidden files blocked
Configuration Examples
Single Site
[[sites]]
name = "website"
hostname = "localhost"
port = 8080
static_dir = "public"
[sites.headers]
"Cache-Control" = "public, max-age=86400"
"X-Content-Type-Options" = "nosniff"
Multiple Asset Directories
# Main site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "dist"
# CDN-like asset server
[[sites]]
name = "assets"
hostname = "assets.localhost"
port = 8081
static_dir = "assets"
[sites.headers]
"Cache-Control" = "public, max-age=31536000"
"Access-Control-Allow-Origin" = "*"
Development vs Production
# Development
[[sites]]
name = "dev"
hostname = "localhost"
port = 8080
static_dir = "src"
[sites.headers]
"Cache-Control" = "no-cache, no-store, must-revalidate"
"X-Environment" = "development"
# Production
[[sites]]
name = "prod"
hostname = "example.com"
port = 8080
static_dir = "build"
[sites.headers]
"Cache-Control" = "public, max-age=31536000"
"X-Environment" = "production"
Testing Static Files
Basic File Serving
# Test HTML file
curl -I http://localhost:8080/index.html
# Test CSS file
curl -I http://localhost:8080/styles.css
# Test JavaScript
curl -I http://localhost:8080/app.js
MIME Type Verification
# Check MIME type headers
curl -I http://localhost:8080/image.png | grep "Content-Type"
curl -I http://localhost:8080/app.wasm | grep "Content-Type"
Subdirectory Access
# Test nested files
curl -I http://localhost:8080/assets/css/main.css
curl -I http://localhost:8080/docs/api.html
Index File Testing
# Directory with trailing slash
curl -I http://localhost:8080/docs/
# Directory without trailing slash
curl -I http://localhost:8080/docs
Troubleshooting
File Not Found (404)
- Check file exists in
static_dir - Verify file permissions (readable)
- Check path spelling and case sensitivity
Wrong MIME Type
- File extension not recognized
- Add custom MIME type mapping if needed
- Verify file extension is correct
Caching Issues
- Clear browser cache
- Check
Cache-Controlheaders - Use browser dev tools to verify requests
Permission Errors
# Fix file permissions
chmod -R 644 static/*
chmod 755 static/
# Fix directory permissions
find static/ -type d -exec chmod 755 {} \;
find static/ -type f -exec chmod 644 {} \;
Best Practices
File Organization
- Use descriptive directory names
- Group related files together
- Keep deep nesting to minimum (max 3-4 levels)
Performance
- Optimize images (WebP, AVIF for modern browsers)
- Minify CSS and JavaScript
- Use appropriate file formats
- Implement proper caching strategy
Security
- Don't serve configuration files
- Avoid exposing sensitive data in static files
- Use proper file permissions
- Regular security audits
Next Steps
- Configure Custom Headers for static files
- Set up Health Monitoring
- Learn about Performance Tuning
Reverse Proxy
BWS provides comprehensive reverse proxy functionality with load balancing and WebSocket support.
Basic Configuration
[[sites]]
name = "proxy"
hostname = "api.example.com"
port = 80
static_dir = "static"
[sites.proxy]
enabled = true
# Backend servers
[[sites.proxy.upstreams]]
name = "backend"
url = "http://127.0.0.1:3001"
weight = 1
[[sites.proxy.upstreams]]
name = "backend"
url = "http://127.0.0.1:3002"
weight = 2
# Routes
[[sites.proxy.routes]]
path = "/api/"
upstream = "backend"
strip_prefix = false
# Load balancing
[sites.proxy.load_balancing]
method = "round_robin"
Load Balancing Methods
Round Robin
[sites.proxy.load_balancing]
method = "round_robin" # Distribute requests evenly
Weighted Round Robin
[[sites.proxy.upstreams]]
name = "backend"
url = "http://server1:3001"
weight = 3 # Gets 3x more requests
[[sites.proxy.upstreams]]
name = "backend"
url = "http://server2:3001"
weight = 1 # Gets 1x requests
[sites.proxy.load_balancing]
method = "weighted"
Least Connections
[sites.proxy.load_balancing]
method = "least_connections" # Route to server with fewest active connections
Route Configuration
Path-Based Routing
# API routes to backend
[[sites.proxy.routes]]
path = "/api/"
upstream = "api-backend"
strip_prefix = false # Keep /api/ in forwarded path
# Admin routes to different backend
[[sites.proxy.routes]]
path = "/admin/"
upstream = "admin-backend"
strip_prefix = true # Remove /admin/ from forwarded path
WebSocket Proxying
[[sites.proxy.routes]]
path = "/ws"
upstream = "websocket-backend"
websocket = true # Enable WebSocket support
strip_prefix = false
[[sites.proxy.upstreams]]
name = "websocket-backend"
url = "http://127.0.0.1:3001" # Will proxy WebSocket connections
Header Management
Automatic Headers
[sites.proxy.headers]
add_x_forwarded = true # Add X-Forwarded-For, X-Forwarded-Proto
add_forwarded = true # Add standard Forwarded header
Custom Headers
[sites.proxy.headers.add]
"X-API-Gateway" = "BWS"
"X-Request-ID" = "auto"
[sites.proxy.headers.remove]
"Server" = true
"X-Powered-By" = true
Timeout Configuration
[sites.proxy.timeout]
read = 30 # Read timeout in seconds
write = 30 # Write timeout in seconds
connect = 5 # Connection timeout in seconds
Complete Example
[server]
name = "BWS Proxy Server"
# Main site with mixed static/proxy
[[sites]]
name = "main"
hostname = "example.com"
port = 80
static_dir = "static"
[sites.proxy]
enabled = true
# API backend cluster
[[sites.proxy.upstreams]]
name = "api-cluster"
url = "http://api1.internal:3001"
weight = 2
[[sites.proxy.upstreams]]
name = "api-cluster"
url = "http://api2.internal:3001"
weight = 1
# WebSocket backend
[[sites.proxy.upstreams]]
name = "websocket-backend"
url = "http://ws.internal:3002"
# Route API requests
[[sites.proxy.routes]]
path = "/api/"
upstream = "api-cluster"
strip_prefix = false
# Route WebSocket connections
[[sites.proxy.routes]]
path = "/ws"
upstream = "websocket-backend"
websocket = true
# Load balancing
[sites.proxy.load_balancing]
method = "weighted"
# Timeouts
[sites.proxy.timeout]
read = 30
write = 30
connect = 5
# Headers
[sites.proxy.headers]
add_x_forwarded = true
add_forwarded = true
[sites.proxy.headers.add]
"X-Gateway" = "BWS"
Testing Proxy Setup
Start Backend Services
# Start test backends
python3 -m http.server 3001 &
python3 -m http.server 3002 &
# Or use Node.js
npx http-server -p 3001 &
npx http-server -p 3002 &
Test Load Balancing
# Test multiple requests to see load balancing
for i in {1..10}; do
curl -H "X-Request-ID: $i" http://localhost/api/test
done
# Check which backend handled each request
curl -v http://localhost/api/test
WebSocket Testing
# Test WebSocket connection
npx wscat -c ws://localhost/ws
# Test through proxy
curl --upgrade websocket http://localhost/ws
Monitoring
Health Checks
# Check proxy status
curl http://localhost/api/health
# Monitor backend connectivity
curl -v http://localhost/api/test
Log Analysis
# Monitor proxy logs
tail -f /var/log/bws.log | grep proxy
# Check upstream connections
netstat -an | grep :3001
Production Considerations
High Availability
# Multiple backend servers
[[sites.proxy.upstreams]]
name = "production-api"
url = "http://api1.prod:3001"
weight = 1
[[sites.proxy.upstreams]]
name = "production-api"
url = "http://api2.prod:3001"
weight = 1
[[sites.proxy.upstreams]]
name = "production-api"
url = "http://api3.prod:3001"
weight = 1
# Conservative timeouts
[sites.proxy.timeout]
read = 60
write = 60
connect = 10
Security Headers
[sites.proxy.headers.add]
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "DENY"
"X-XSS-Protection" = "1; mode=block"
[sites.proxy.headers.remove]
"Server" = true
"X-Powered-By" = true
SSL Termination
# Terminate SSL at BWS, HTTP to backends
[[sites]]
name = "ssl-proxy"
hostname = "api.example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["api.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "backend"
url = "http://internal-api:3001" # HTTP to internal backend
Load Balancing in BWS
BWS reverse proxy includes comprehensive load balancing functionality similar to Caddy, with three different algorithms available for both HTTP and WebSocket connections.
Load Balancing Algorithms
1. Round Robin (round_robin)
Distributes requests evenly across all available backend servers in a circular manner.
How it works:
- Maintains a counter for each upstream group
- Increments counter for each request
- Selects server based on
counter % number_of_servers - Provides fair distribution when all servers have equal capacity
Best for:
- Servers with similar performance characteristics
- Simple, predictable load distribution
- Development and testing environments
- WebSocket chat applications with similar server capacity
2. Weighted (weighted)
Distributes requests based on assigned weights, allowing some servers to handle more traffic.
How it works:
- Each server has a weight value (default: 1)
- Random selection weighted by server capacity
- Higher weight = more requests
- Uses fast random number generation for selection
Best for:
- Servers with different capacities
- Gradual traffic migration
- Performance-based load distribution
- WebSocket servers with varying performance characteristics
3. Least Connections (least_connections)
Routes requests to the server with the fewest active connections.
How it works:
- Tracks active connections per server using atomic counters
- Increments counter when request starts
- Decrements counter when request completes
- Always routes to server with minimum connections
Best for:
- Long-running requests
- Servers with varying response times
- Optimal connection distribution
- WebSocket connections with persistent sessions
WebSocket Load Balancing
BWS extends all load balancing algorithms to support WebSocket connections with the same efficiency and reliability as HTTP requests.
WebSocket-Specific Features
- Automatic Protocol Detection: BWS detects WebSocket upgrade requests and applies load balancing seamlessly
- URL Transformation: HTTP upstream URLs are automatically converted to WebSocket URLs
http://localhost:3001โws://localhost:3001https://localhost:3001โwss://localhost:3001
- Persistent Connection Tracking: WebSocket connections are tracked for least-connections algorithm
- Same Algorithms: All three load balancing methods work identically for WebSocket connections
WebSocket Configuration Example
[[sites]]
name = "websocket-app"
hostname = "ws.example.com"
port = 8080
[sites.proxy]
enabled = true
# WebSocket upstream servers
[[sites.proxy.upstreams]]
name = "websocket_servers"
url = "http://localhost:3001" # Automatically becomes ws://localhost:3001
weight = 1
[[sites.proxy.upstreams]]
name = "websocket_servers"
url = "http://localhost:3002" # Automatically becomes ws://localhost:3002
weight = 2 # Higher weight for better server
# WebSocket routes with load balancing
[[sites.proxy.routes]]
path = "/ws/chat"
upstream = "websocket_servers"
strip_prefix = true
websocket = true # Enable WebSocket proxying
[[sites.proxy.routes]]
path = "/ws/notifications"
upstream = "websocket_servers"
strip_prefix = false
websocket = true
# Load balancing applies to WebSocket connections
[sites.proxy.load_balancing]
method = "least_connections" # Ideal for persistent WebSocket connections
Use Cases by Algorithm
Round Robin for WebSocket:
- Chat rooms with equal server capacity
- Broadcasting services
- Simple real-time APIs
Weighted for WebSocket:
- Mixed server hardware configurations
- Gradual migration to new WebSocket servers
- Performance-based distribution
Least Connections for WebSocket:
- Long-lived gaming connections
- Persistent monitoring sessions
- Real-time collaboration tools
Configuration
Basic Setup
# Site configuration
[[sites]]
name = "example.com"
hostname = "localhost"
port = 8080
[sites.proxy]
enabled = true
# Multiple servers with same upstream name
[[sites.proxy.upstreams]]
name = "backend-servers"
url = "http://127.0.0.1:3001"
weight = 1
[[sites.proxy.upstreams]]
name = "backend-servers"
url = "http://127.0.0.1:3002"
weight = 2 # This server gets 2x traffic in weighted mode
[[sites.proxy.upstreams]]
name = "backend-servers"
url = "http://127.0.0.1:3003"
weight = 1
# Route configuration
[[sites.proxy.routes]]
path = "/api/"
upstream = "backend-servers"
# Load balancing method
[sites.proxy.load_balancing]
method = "round_robin" # or "weighted" or "least_connections"
Advanced Configuration
[sites.proxy]
enabled = true
# Request timeout
timeout = { read = 30, write = 30 }
# Header management
[sites.proxy.headers]
add_x_forwarded = true
add_forwarded = true
[sites.proxy.headers.add]
"X-Custom-Header" = "BWS-Proxy"
[sites.proxy.headers.remove]
"X-Internal-Header" = true
Testing Load Balancing
- Set up backend servers on different ports (3001, 3002, 3003)
- Configure BWS with the load balancing configuration
- Make multiple requests to see distribution
- Monitor logs to verify load balancing behavior
Use the included test script:
./tests/test_load_balance.sh
Implementation Details
Thread Safety
- Round-robin counters use
AtomicUsizefor thread-safe access - Connection tracking uses atomic operations
- No locks needed for load balancing decisions
Performance
- O(1) complexity for round-robin selection
- O(n) complexity for weighted selection (where n = number of servers)
- O(n) complexity for least connections (where n = number of servers)
- Minimal overhead with atomic operations
Reliability
- Automatic failover if upstream server is unavailable
- Connection tracking prevents memory leaks
- Graceful handling of server addition/removal
Monitoring
BWS provides detailed logging for load balancing decisions:
INFO: Proxying request /api/users to upstream 'backend-servers'
INFO: Selected server http://127.0.0.1:3002 using round_robin
INFO: Successfully proxied request /api/users to http://127.0.0.1:3002
Connection counts and load balancing decisions are logged at debug level for troubleshooting.
Comparison with Caddy
BWS load balancing is designed to be compatible with Caddy's approach:
| Feature | BWS | Caddy |
|---|---|---|
| Round Robin | โ | โ |
| Weighted | โ | โ |
| Least Connections | โ | โ |
| Health Checks | ๐ Planned | โ |
| Sticky Sessions | ๐ Planned | โ |
| Circuit Breaker | ๐ Planned | โ |
Future Enhancements
- Health Checks: Automatic detection of unhealthy servers
- Sticky Sessions: Route requests from same client to same server
- Circuit Breaker: Temporary removal of failing servers
- Metrics: Detailed statistics on load balancing performance
- Dynamic Configuration: Runtime modification of upstream servers
Hot Reload & Zero-Downtime Configuration Updates
BWS implements a production-grade master-worker architecture that enables true hot reloading of configuration without dropping connections or interrupting service. This feature is essential for production environments where service availability is critical.
Architecture Overview
BWS uses a master-worker process model similar to nginx, HAProxy, and other enterprise-grade web servers:
Master Process (PID 1234)
โโโ Configuration monitoring
โโโ Signal handling
โโโ Worker lifecycle management
โโโ Graceful shutdown coordination
Worker Process (PID 5678)
โโโ HTTP request handling
โโโ SSL/TLS termination
โโโ Proxy and load balancing
โโโ Static file serving
How It Works
- Master Process: Monitors configuration files and manages worker processes
- Worker Processes: Handle actual HTTP traffic and connections
- Hot Reload: Master spawns new workers with updated config while old workers gracefully finish
- Zero Downtime: No connection drops or service interruption during configuration updates
Master-Worker Architecture
Master Process Responsibilities
- Monitor configuration file changes
- Handle reload signals (SIGHUP)
- Spawn new worker processes
- Gracefully terminate old workers
- Coordinate shutdown procedures
Worker Process Responsibilities
- Process HTTP requests
- Handle SSL/TLS connections
- Serve static files
- Proxy requests to backends
- Execute middleware
Process Lifecycle
graph TD
A[Master Starts] --> B[Spawn Initial Worker]
B --> C[Worker Serves Traffic]
C --> D[SIGHUP Received]
D --> E[Load New Config]
E --> F[Validate Config]
F --> G[Spawn New Worker]
G --> H[New Worker Ready]
H --> I[Graceful Old Worker Shutdown]
I --> C
Configuration Hot Reload
Triggering Hot Reload
Hot reload can be triggered in several ways:
Using Unix Signals
# Find master process
pgrep -f "bws.*master"
# Send SIGHUP for hot reload
kill -HUP $(pgrep -f "bws.*master")
Using systemd
# Reload configuration through systemd
sudo systemctl reload bws
Using Process Management
# Direct signal to specific PID
kill -HUP 1234
What Can Be Hot Reloaded
โ Supported (Zero Downtime):
- Site configurations
- Hostname mappings
- Multi-hostname configurations
- SSL certificate paths
- ACME settings
- Proxy upstream configurations
- Load balancing algorithms
- Static file directories
- Security headers
- Middleware configurations
- Logging settings
- Health check configurations
โ Not Supported (Requires Restart):
- Server listen ports
- Worker process count
- Core server settings
- TLS protocol versions
Configuration Validation
Before applying changes, BWS validates the new configuration:
# The master process automatically:
# 1. Loads new configuration file
# 2. Validates syntax and semantics
# 3. Checks file permissions and paths
# 4. Verifies SSL certificate validity
# 5. Tests upstream connectivity (optional)
If validation fails, the master process keeps the existing configuration and logs an error.
Hot Reload Process
Step-by-Step Flow
-
Signal Reception
Master Process: Received SIGHUP signal -
Configuration Loading
Master Process: Loading configuration from /etc/bws/config.toml -
Validation
Master Process: Validating new configuration -
Worker Spawning
Master Process: Spawning new worker with PID 9876 -
Traffic Transition
New Worker: Ready to accept connections -
Graceful Shutdown
Master Process: Sending SIGTERM to old worker (PID 5678) Old Worker: Finishing existing connections Old Worker: Graceful shutdown complete
Error Handling
If hot reload fails:
- Invalid configuration: Master logs error and keeps current config
- Worker spawn failure: Master retries and falls back to current worker
- Resource exhaustion: Master logs warning and maintains current worker
Configuration Examples
Basic Hot Reload Setup
# config.toml
[server]
name = "BWS Production Server"
[[sites]]
name = "main-site"
hostname = "example.com"
port = 8080
static_dir = "/var/www/html"
# Add new site without restart
[[sites]]
name = "api-site"
hostname = "api.example.com"
port = 8080
static_dir = "/var/www/api"
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "backend"
url = "http://192.168.1.100:3000"
Multi-Hostname Hot Reload
[[sites]]
name = "company-main"
hostname = "company.com"
# Add new hostnames without restart
hostnames = [
"www.company.com",
"company.net", # New hostname
"www.company.net" # New hostname
]
port = 8080
static_dir = "/var/www/company"
SSL Configuration Hot Reload
[[sites]]
name = "secure-site"
hostname = "secure.example.com"
port = 443
[sites.ssl]
enabled = true
# Update certificate paths without restart
cert_file = "/etc/ssl/certs/new-cert.pem"
key_file = "/etc/ssl/private/new-key.pem"
[sites.ssl.acme]
enabled = true
# Update ACME settings without restart
email = "security@example.com"
Proxy Configuration Hot Reload
[[sites]]
name = "proxy-site"
hostname = "proxy.example.com"
port = 8080
[sites.proxy]
enabled = true
# Add new upstreams without restart
[[sites.proxy.upstreams]]
name = "backend-1"
url = "http://192.168.1.101:3000"
weight = 3
[[sites.proxy.upstreams]]
name = "backend-2"
url = "http://192.168.1.102:3000"
weight = 2
# Update routing without restart
[[sites.proxy.routes]]
path = "/api/v1/"
upstream = "backend-1"
[[sites.proxy.routes]]
path = "/api/v2/"
upstream = "backend-2"
Monitoring Hot Reload
Process Monitoring
# View process tree
pstree -p $(pgrep -f "bws.*master")
# Monitor process status
watch "ps aux | grep bws"
# Check process relationships
ps -eo pid,ppid,cmd | grep bws
Log Monitoring
# Follow hot reload logs
tail -f /var/log/bws/bws.log | grep -E "(reload|worker|master)"
# Filter for specific events
journalctl -u bws -f | grep "Hot reload"
Health Checking
# Verify service is responding
curl -I http://localhost:8080/health
# Check configuration version
curl -s http://localhost:8080/api/health | jq '.config_version'
# Test new configuration features
curl -H "Host: new-site.com" http://localhost:8080/
Production Considerations
Performance Impact
- Worker Spawn Time: ~100-500ms depending on configuration complexity
- Memory Usage: Brief spike during worker transition
- CPU Impact: Minimal, mostly during config validation
- Connection Handling: Zero dropped connections
Best Practices
-
Configuration Validation
# Always validate before reload bws --config-check /etc/bws/config.toml -
Gradual Rollouts
# Test on staging first # Use monitoring to verify health # Roll back quickly if issues arise -
Monitoring Setup
# Monitor process counts # Track memory usage # Alert on failed reloads -
Backup Configurations
# Version control configurations # Keep rollback configs ready # Document all changes
Systemd Integration
# /etc/systemd/system/bws.service
[Unit]
Description=BWS Web Server
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Automated Hot Reload
#!/bin/bash
# hot-reload-on-config-change.sh
CONFIG_FILE="/etc/bws/config.toml"
MASTER_PID=$(pgrep -f "bws.*master")
# Watch for config file changes
inotifywait -m -e modify "$CONFIG_FILE" | while read event; do
echo "Configuration file changed, triggering hot reload..."
# Validate configuration
if bws --config-check "$CONFIG_FILE"; then
echo "Configuration valid, sending SIGHUP"
kill -HUP "$MASTER_PID"
else
echo "Configuration invalid, skipping reload"
fi
done
Troubleshooting
Common Issues
Master Process Not Found
# Check if BWS is running
systemctl status bws
# Look for all BWS processes
ps aux | grep bws
Configuration Not Reloading
# Check configuration syntax
bws --config-check /etc/bws/config.toml
# Verify file permissions
ls -la /etc/bws/config.toml
# Check logs for errors
journalctl -u bws | tail -20
Worker Process Issues
# Check process tree
pstree -p $(pgrep -f "bws.*master")
# Monitor worker spawning
tail -f /var/log/bws/bws.log | grep worker
Debug Mode
# Start with verbose logging
RUST_LOG=debug bws --config config.toml
# Enable hot reload debugging
RUST_LOG=bws::core::hot_reload=trace bws --config config.toml
Recovery Procedures
Failed Hot Reload
# Check current configuration
curl http://localhost:8080/api/health
# Restart if necessary
systemctl restart bws
Stuck Worker Process
# Force kill unresponsive worker
kill -9 $(pgrep -f "bws.*worker")
# Master will spawn new worker automatically
Integration Examples
With Load Balancer
# HAProxy health check during reload
backend bws_servers
option httpchk GET /health
server bws1 127.0.0.1:8080 check
With Monitoring
# Prometheus metrics for hot reload events
bws_hot_reload_total{status="success"} 15
bws_hot_reload_total{status="failed"} 1
bws_worker_spawn_duration_seconds 0.342
With CI/CD Pipeline
# Deploy script
- name: Update BWS Configuration
run: |
# Validate new config
bws --config-check new-config.toml
# Deploy config
cp new-config.toml /etc/bws/config.toml
# Hot reload
systemctl reload bws
# Verify deployment
curl -f http://localhost:8080/health
Next Steps
- Learn about Production Deployment
- Set up Monitoring and Alerting
- Configure SSL/TLS Management
- Explore Load Balancing
Custom Headers Configuration
BWS allows you to configure custom HTTP headers for responses, providing control over caching, security, and client behavior.
Header Configuration
Headers are configured per site in the config.toml file:
[[sites]]
name = "example"
hostname = "localhost"
port = 8080
static_dir = "static"
[sites.headers]
"Cache-Control" = "public, max-age=3600"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
Common Header Types
Security Headers
Content Security Policy (CSP)
[sites.headers]
"Content-Security-Policy" = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
Frame Protection
[sites.headers]
"X-Frame-Options" = "DENY" # Block all framing
"X-Frame-Options" = "SAMEORIGIN" # Allow same-origin framing
"X-Frame-Options" = "ALLOW-FROM https://example.com" # Allow specific origin
Content Type Protection
[sites.headers]
"X-Content-Type-Options" = "nosniff" # Prevent MIME sniffing
XSS Protection
[sites.headers]
"X-XSS-Protection" = "1; mode=block"
HTTPS Enforcement
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
Caching Headers
Standard Caching
[sites.headers]
"Cache-Control" = "public, max-age=3600" # 1 hour
"Cache-Control" = "public, max-age=86400" # 1 day
"Cache-Control" = "public, max-age=31536000" # 1 year
No Caching
[sites.headers]
"Cache-Control" = "no-cache, no-store, must-revalidate"
"Pragma" = "no-cache"
"Expires" = "0"
ETag Support
[sites.headers]
"ETag" = ""custom-etag-value""
CORS Headers
Basic CORS
[sites.headers]
"Access-Control-Allow-Origin" = "*"
"Access-Control-Allow-Methods" = "GET, POST, PUT, DELETE, OPTIONS"
"Access-Control-Allow-Headers" = "Content-Type, Authorization"
Restricted CORS
[sites.headers]
"Access-Control-Allow-Origin" = "https://example.com"
"Access-Control-Allow-Credentials" = "true"
"Access-Control-Max-Age" = "86400"
Custom Application Headers
API Versioning
[sites.headers]
"X-API-Version" = "v1.0.0"
"X-Service-Name" = "BWS"
Environment Information
[sites.headers]
"X-Environment" = "production"
"X-Deploy-Version" = "2024.01.15"
Rate Limiting Info
[sites.headers]
"X-RateLimit-Limit" = "1000"
"X-RateLimit-Window" = "3600"
Site-Specific Configurations
Development Environment
[[sites]]
name = "dev"
hostname = "localhost"
port = 8080
static_dir = "src"
[sites.headers]
"Cache-Control" = "no-cache, no-store, must-revalidate"
"X-Environment" = "development"
"X-Debug-Mode" = "enabled"
"Access-Control-Allow-Origin" = "*"
"Access-Control-Allow-Methods" = "GET, POST, PUT, DELETE, OPTIONS"
Production Environment
[[sites]]
name = "prod"
hostname = "example.com"
port = 8080
static_dir = "dist"
[sites.headers]
"Cache-Control" = "public, max-age=31536000"
"X-Environment" = "production"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
"Content-Security-Policy" = "default-src 'self'"
API Server
[[sites]]
name = "api"
hostname = "api.example.com"
port = 8081
static_dir = "api-docs"
[sites.headers]
"Content-Type" = "application/json"
"X-API-Version" = "v2.1.0"
"Access-Control-Allow-Origin" = "https://app.example.com"
"Access-Control-Allow-Methods" = "GET, POST, PUT, DELETE"
"Access-Control-Allow-Headers" = "Content-Type, Authorization, X-Requested-With"
"Access-Control-Max-Age" = "86400"
"Cache-Control" = "no-cache"
CDN/Asset Server
[[sites]]
name = "cdn"
hostname = "cdn.example.com"
port = 8082
static_dir = "assets"
[sites.headers]
"Cache-Control" = "public, max-age=31536000, immutable"
"Access-Control-Allow-Origin" = "*"
"X-Content-Type-Options" = "nosniff"
"Vary" = "Accept-Encoding"
Advanced Header Patterns
Multi-Value Headers
Some headers can have multiple values separated by commas:
[sites.headers]
"Vary" = "Accept-Encoding, Accept-Language, User-Agent"
"Access-Control-Expose-Headers" = "X-Total-Count, X-Page-Count"
Conditional Headers
Different headers for different file types (handled by custom logic):
# Base headers for all files
[sites.headers]
"X-Served-By" = "BWS"
"X-Content-Type-Options" = "nosniff"
# Note: File-specific headers would require application logic
# This is the base configuration that applies to all responses
Header Testing
Check Headers with curl
# View all response headers
curl -I http://localhost:8080/
# Check specific header
curl -I http://localhost:8080/ | grep "Cache-Control"
# Verbose output with request/response
curl -v http://localhost:8080/
Check Headers with HTTPie
# View headers
http HEAD localhost:8080
# Check specific response
http GET localhost:8080/api/health
Browser Developer Tools
- Open DevTools (F12)
- Navigate to Network tab
- Reload page
- Click on any request
- View Response Headers section
Security Best Practices
Minimal Security Headers
[sites.headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"
"Referrer-Policy" = "strict-origin-when-cross-origin"
Enhanced Security Headers
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains; preload"
"Content-Security-Policy" = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"
"Referrer-Policy" = "strict-origin-when-cross-origin"
"Permissions-Policy" = "camera=(), microphone=(), geolocation=()"
API Security Headers
[sites.headers]
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "DENY"
"Cache-Control" = "no-store"
"Content-Security-Policy" = "default-src 'none'"
"X-Permitted-Cross-Domain-Policies" = "none"
Performance Headers
Optimal Caching Strategy
# Static assets (CSS, JS, images)
[sites.headers]
"Cache-Control" = "public, max-age=31536000, immutable"
"Vary" = "Accept-Encoding"
# HTML files
# Cache-Control = "public, max-age=3600" # Shorter cache
# API responses
# Cache-Control = "no-cache" # No caching
Compression Headers
[sites.headers]
"Vary" = "Accept-Encoding"
"Content-Encoding" = "gzip" # If serving pre-compressed files
Troubleshooting
Headers Not Appearing
- Check TOML syntax in config file
- Restart BWS after configuration changes
- Verify header names are correct (case-sensitive)
- Use browser dev tools to inspect
CORS Issues
# Debug CORS headers
[sites.headers]
"Access-Control-Allow-Origin" = "*"
"Access-Control-Allow-Methods" = "GET, POST, PUT, DELETE, OPTIONS"
"Access-Control-Allow-Headers" = "Content-Type, Authorization, X-Requested-With"
"Access-Control-Max-Age" = "86400"
CSP Violations
- Start with permissive policy
- Use browser console to identify violations
- Gradually restrict policy
- Test thoroughly
Cache Problems
# Force no-cache for debugging
[sites.headers]
"Cache-Control" = "no-cache, no-store, must-revalidate"
"Pragma" = "no-cache"
"Expires" = "0"
Common Header Combinations
Static Website
[sites.headers]
"Cache-Control" = "public, max-age=3600"
"X-Frame-Options" = "SAMEORIGIN"
"X-Content-Type-Options" = "nosniff"
"Referrer-Policy" = "strict-origin-when-cross-origin"
Single Page Application (SPA)
[sites.headers]
"Cache-Control" = "public, max-age=3600"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"Content-Security-Policy" = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
Documentation Site
[sites.headers]
"Cache-Control" = "public, max-age=1800"
"X-Frame-Options" = "SAMEORIGIN"
"X-Content-Type-Options" = "nosniff"
"Content-Security-Policy" = "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:"
Next Steps
- Learn about Health Monitoring
- Configure Docker Deployment
- Explore Performance Tuning
Health Monitoring
BWS provides built-in health monitoring endpoints and logging capabilities to help you monitor your server's status and performance.
Health Endpoints
BWS automatically provides health check endpoints for monitoring:
Basic Health Check
GET /health
Response:
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00Z",
"version": "0.1.5",
"uptime": 3600
}
Detailed Health Status
GET /health/detailed
Response:
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00Z",
"version": "0.1.5",
"uptime": 3600,
"sites": [
{
"name": "main",
"hostname": "localhost",
"port": 8080,
"status": "active",
"requests_served": 1542,
"last_request": "2024-01-15T10:29:45Z"
}
],
"system": {
"memory_usage": "45.2 MB",
"cpu_usage": "12.5%",
"disk_usage": "78.3%"
}
}
Monitoring Configuration
Health Check Settings
[monitoring]
enabled = true
health_endpoint = "/health"
detailed_endpoint = "/health/detailed"
metrics_endpoint = "/metrics"
Custom Health Checks
[monitoring.checks]
disk_threshold = 90 # Alert if disk usage > 90%
memory_threshold = 80 # Alert if memory usage > 80%
response_time_threshold = 1000 # Alert if response time > 1000ms
Logging Configuration
Basic Logging Setup
[logging]
level = "info"
format = "json"
output = "stdout"
File Logging
[logging]
level = "info"
format = "json"
output = "file"
file_path = "/var/log/bws/bws.log"
max_size = "100MB"
max_files = 10
compress = true
Structured Logging
[logging]
level = "debug"
format = "json"
include_fields = [
"timestamp",
"level",
"message",
"request_id",
"client_ip",
"user_agent",
"response_time",
"status_code"
]
Log Levels
Available Levels
- ERROR: Error conditions and failures
- WARN: Warning conditions
- INFO: General informational messages
- DEBUG: Detailed debugging information
- TRACE: Very detailed tracing information
Log Level Examples
# Production
[logging]
level = "info"
# Development
[logging]
level = "debug"
# Troubleshooting
[logging]
level = "trace"
Metrics Collection
Basic Metrics
BWS automatically collects:
- Request count
- Response times
- Error rates
- Active connections
- Memory usage
- CPU usage
Prometheus Integration
[monitoring.prometheus]
enabled = true
endpoint = "/metrics"
port = 9090
Example metrics output:
# HELP bws_requests_total Total number of requests
# TYPE bws_requests_total counter
bws_requests_total{site="main",method="GET",status="200"} 1542
# HELP bws_response_time_seconds Response time in seconds
# TYPE bws_response_time_seconds histogram
bws_response_time_seconds_bucket{site="main",le="0.1"} 1200
bws_response_time_seconds_bucket{site="main",le="0.5"} 1500
bws_response_time_seconds_bucket{site="main",le="1.0"} 1540
Alerting
Health Check Monitoring
#!/bin/bash
# health_check.sh
HEALTH_URL="http://localhost:8080/health"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL)
if [ $RESPONSE -eq 200 ]; then
echo "BWS is healthy"
exit 0
else
echo "BWS health check failed: HTTP $RESPONSE"
exit 1
fi
Uptime Monitoring Script
#!/bin/bash
# uptime_check.sh
while true; do
if curl -f -s http://localhost:8080/health > /dev/null; then
echo "$(date): BWS is running"
else
echo "$(date): BWS is DOWN" | mail -s "BWS Alert" admin@example.com
fi
sleep 60
done
System Integration
Systemd Integration
# /etc/systemd/system/bws.service
[Unit]
Description=BWS Web Server
After=network.target
[Service]
Type=simple
User=bws
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml
Restart=always
RestartSec=5
# Health check
ExecHealthCheck=/usr/local/bin/health_check.sh
HealthCheckInterval=30s
[Install]
WantedBy=multi-user.target
Docker Health Checks
# Dockerfile
FROM rust:1.89-slim
# ... build steps ...
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
EXPOSE 8080
CMD ["bws", "--config", "/app/config.toml"]
Docker Compose Health Check
# docker-compose.yml
version: '3.8'
services:
bws:
build: .
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
Monitoring Tools Integration
Grafana Dashboard
{
"dashboard": {
"title": "BWS Monitoring",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(bws_requests_total[5m])"
}
]
},
{
"title": "Response Time",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, bws_response_time_seconds)"
}
]
}
]
}
}
Nagios Check
#!/bin/bash
# check_bws.sh for Nagios
HOST="localhost"
PORT="8080"
WARNING_TIME=1000
CRITICAL_TIME=2000
RESPONSE_TIME=$(curl -o /dev/null -s -w "%{time_total}" http://$HOST:$PORT/health)
RESPONSE_TIME_MS=$(echo "$RESPONSE_TIME * 1000" | bc)
if (( $(echo "$RESPONSE_TIME_MS > $CRITICAL_TIME" | bc -l) )); then
echo "CRITICAL - Response time: ${RESPONSE_TIME_MS}ms"
exit 2
elif (( $(echo "$RESPONSE_TIME_MS > $WARNING_TIME" | bc -l) )); then
echo "WARNING - Response time: ${RESPONSE_TIME_MS}ms"
exit 1
else
echo "OK - Response time: ${RESPONSE_TIME_MS}ms"
exit 0
fi
Log Analysis
Log Parsing with jq
# Extract error logs
cat bws.log | jq 'select(.level == "ERROR")'
# Count requests by status code
cat bws.log | jq -r '.status_code' | sort | uniq -c
# Average response time
cat bws.log | jq -r '.response_time' | awk '{sum+=$1; count++} END {print sum/count}'
Log Aggregation
# Tail logs in real-time
tail -f /var/log/bws/bws.log | jq '.'
# Filter by log level
tail -f /var/log/bws/bws.log | jq 'select(.level == "ERROR" or .level == "WARN")'
# Monitor specific endpoints
tail -f /var/log/bws/bws.log | jq 'select(.path == "/api/users")'
Performance Monitoring
Request Tracking
[monitoring.requests]
track_response_times = true
track_status_codes = true
track_user_agents = true
track_client_ips = true
sample_rate = 1.0 # 100% sampling
Memory Monitoring
[monitoring.memory]
track_usage = true
alert_threshold = 80 # Alert at 80% usage
gc_metrics = true
Connection Monitoring
[monitoring.connections]
track_active = true
track_total = true
max_connections = 1000
timeout = 30 # seconds
Troubleshooting Health Issues
Common Health Check Failures
Service Unavailable
# Check if BWS is running
ps aux | grep bws
# Check port binding
netstat -tulpn | grep :8080
# Check configuration
bws --config-check
High Response Times
# Check system load
top
htop
# Check disk usage
df -h
# Check memory usage
free -h
Memory Leaks
# Monitor memory over time
while true; do
ps -o pid,ppid,cmd,%mem,%cpu -p $(pgrep bws)
sleep 10
done
# Generate memory dump (if available)
kill -USR1 $(pgrep bws)
Health Check Debugging
# Test health endpoint
curl -v http://localhost:8080/health
# Check detailed health
curl -s http://localhost:8080/health/detailed | jq '.'
# Monitor health over time
while true; do
echo "$(date): $(curl -s http://localhost:8080/health | jq -r '.status')"
sleep 30
done
Best Practices
Health Check Configuration
- Set appropriate timeouts (3-5 seconds)
- Use consistent intervals (30-60 seconds)
- Include multiple health indicators
- Monitor both application and system metrics
Logging Strategy
- Use structured logging (JSON format)
- Include correlation IDs for request tracking
- Log at appropriate levels
- Rotate logs to prevent disk space issues
Alerting Guidelines
- Set realistic thresholds
- Avoid alert fatigue
- Include actionable information
- Test alert mechanisms regularly
Monitoring Coverage
- Monitor all critical endpoints
- Track business metrics, not just technical
- Set up both reactive and proactive monitoring
- Document monitoring procedures
Next Steps
- Configure Docker Deployment with health checks
- Set up Production Environment monitoring
- Learn about Performance Tuning
Security
BWS provides comprehensive security features to protect your web applications and server infrastructure.
Management API Security
BWS includes a secure Management API for administrative operations like configuration reloading. This API is designed with security-first principles.
Localhost-Only Access
The Management API runs on a separate port and binds exclusively to localhost (127.0.0.1) to prevent external access:
[management]
enabled = true
host = "127.0.0.1" # Localhost only - cannot be changed
port = 7654 # Configurable port
Security Benefits:
- ๐ No External Access: Only accessible from the server itself
- ๐ Isolated Service: Separate from main web server ports
- ๐ IP Validation: Double-checks request origin is localhost
API Key Authentication
For additional security, you can enable API key authentication:
[management]
enabled = true
host = "127.0.0.1"
port = 7654
api_key = "your-secure-api-key-here"
Usage with API Key:
# Config reload with API key
curl -X POST http://127.0.0.1:7654/api/config/reload \
-H "X-API-Key: your-secure-api-key-here"
Without API Key:
# Config reload (localhost only)
curl -X POST http://127.0.0.1:7654/api/config/reload
Available Endpoints
| Endpoint | Method | Description | Authentication |
|---|---|---|---|
/api/config/reload | POST | Reload configuration | Optional API Key |
Security Logging
All Management API requests are logged with client IP addresses:
[INFO] Management API: Config reload requested
[INFO] Configuration reloaded successfully via management API
[INFO] Management API request: POST /api/config/reload from 127.0.0.1:35528
General Security Features
Request Size Limits
Protect against oversized requests:
[security]
max_request_size = "10MB"
HTTPS/TLS Support
BWS supports modern TLS configurations:
[sites.ssl]
enabled = true
cert_path = "./certs/domain.crt"
key_path = "./certs/domain.key"
auto_cert = true # ACME/Let's Encrypt support
Custom Security Headers
Add security headers to responses:
[sites.headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
"Content-Security-Policy" = "default-src 'self'"
Access Control
Configure CORS and access control:
[sites.access_control]
enabled = true
allow_origins = ["https://example.com", "https://app.example.com"]
allow_methods = ["GET", "POST", "PUT", "DELETE"]
allow_headers = ["Content-Type", "Authorization"]
max_age = 3600
Production Security Checklist
Management API
- Enable API key authentication in production
- Ensure port 7654 is not exposed externally via firewall
- Monitor management API access logs
- Rotate API keys regularly
General Security
- Use HTTPS/TLS for all sites
- Configure appropriate security headers
- Set reasonable request size limits
- Enable access logging
- Regular security updates
Network Security
- Configure firewall rules
- Use reverse proxy if needed
- Monitor unusual traffic patterns
- Implement rate limiting if required
Security Best Practices
- Principle of Least Privilege: Only enable features you need
- Defense in Depth: Use multiple security layers
- Regular Updates: Keep BWS and dependencies updated
- Monitoring: Enable comprehensive logging
- Testing: Regularly test security configurations
Reporting Security Issues
If you discover a security vulnerability in BWS, please report it responsibly:
- Do not open a public GitHub issue
- Contact the maintainers privately
- Provide detailed reproduction steps
- Allow time for the issue to be fixed before disclosure
Your security reports help make BWS safer for everyone.
Docker Deployment
BWS provides official Docker images for easy deployment.
Quick Start
Run with Default Configuration
# Run BWS on port 8080
docker run -p 8080:8080 ghcr.io/benliao/bws:latest
Run with Custom Configuration
# Run with custom config and static files
docker run -d \
--name bws \
-p 8080:8080 \
-v $(pwd)/config.toml:/app/config.toml:ro \
-v $(pwd)/static:/app/static:ro \
ghcr.io/benliao/bws:latest
Available Images
Docker Hub Tags
latest- Latest stable releasev0.3.5- Specific version (recommended for production)main- Latest development build
Multi-Architecture Support
Images support multiple architectures:
linux/amd64(x86_64)linux/arm64(ARM64/Apple Silicon)
Configuration Examples
Single Static Site
# Create content
mkdir static
echo "<h1>Hello from Docker!</h1>" > static/index.html
# Run container
docker run -d \
--name bws-static \
-p 8080:8080 \
-v $(pwd)/static:/app/static:ro \
ghcr.io/benliao/bws:latest \
bws /app/static --port 8080
Multi-Site with SSL
# Create configuration
cat > config.toml << 'EOF'
[server]
name = "Docker Multi-Site"
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "/app/static"
default = true
[[sites]]
name = "secure"
hostname = "secure.localhost"
port = 8443
static_dir = "/app/static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.localhost"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
challenge_dir = "/app/acme-challenges"
EOF
# Create directories
mkdir -p static acme-challenges
# Run container
docker run -d \
--name bws-multisite \
-p 8080:8080 \
-p 8443:8443 \
-v $(pwd)/config.toml:/app/config.toml:ro \
-v $(pwd)/static:/app/static:ro \
-v $(pwd)/acme-challenges:/app/acme-challenges \
ghcr.io/benliao/bws:latest
Docker Compose
Basic Setup
# docker-compose.yml
version: '3.8'
services:
bws:
image: ghcr.io/benliao/bws:latest
container_name: bws
ports:
- "8080:8080"
- "8443:8443"
volumes:
- ./config.toml:/app/config.toml:ro
- ./static:/app/static:ro
- ./acme-challenges:/app/acme-challenges
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"]
interval: 30s
timeout: 10s
retries: 3
Production Setup with Reverse Proxy
version: '3.8'
services:
bws:
image: ghcr.io/benliao/bws:v0.3.5
container_name: bws
expose:
- "8080"
volumes:
- ./config.toml:/app/config.toml:ro
- ./static:/app/static:ro
- ./acme-challenges:/app/acme-challenges
restart: unless-stopped
networks:
- bws-network
nginx:
image: nginx:alpine
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl-certs:/etc/ssl/certs:ro
depends_on:
- bws
restart: unless-stopped
networks:
- bws-network
networks:
bws-network:
driver: bridge
Start with compose:
docker-compose up -d
Build Custom Image
Dockerfile
FROM ghcr.io/benliao/bws:latest
# Copy custom configuration
COPY config.toml /app/config.toml
COPY static/ /app/static/
# Expose ports
EXPOSE 8080 8443
# Run BWS
CMD ["bws", "--config", "/app/config.toml"]
Build and Run
# Build custom image
docker build -t my-bws .
# Run custom image
docker run -d --name my-bws-container -p 8080:8080 my-bws
Volume Mounts
Important Directories
/app/config.toml- Configuration file/app/static/- Static content directory/app/acme-challenges/- ACME challenge directory/app/certs/- Manual SSL certificates
Example with All Mounts
docker run -d \
--name bws-full \
-p 80:80 \
-p 443:443 \
-v $(pwd)/config.toml:/app/config.toml:ro \
-v $(pwd)/static:/app/static:ro \
-v $(pwd)/certs:/app/certs:ro \
-v $(pwd)/acme-challenges:/app/acme-challenges \
-v $(pwd)/logs:/app/logs \
ghcr.io/benliao/bws:latest
Environment Variables
# Override config with environment variables
docker run -d \
--name bws-env \
-p 8080:8080 \
-e BWS_LOG_LEVEL=debug \
-e BWS_WORKERS=4 \
-v $(pwd)/static:/app/static:ro \
ghcr.io/benliao/bws:latest \
bws /app/static --port 8080
Health Checks
Docker Health Check
# Run with health check
docker run -d \
--name bws-health \
-p 8080:8080 \
--health-cmd="curl -f http://localhost:8080/api/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
ghcr.io/benliao/bws:latest
Check Container Health
# Check health status
docker ps
docker inspect bws-health | grep -A 10 '"Health"'
Troubleshooting
Common Issues
Container Won't Start:
# Check logs
docker logs bws
# Validate configuration
docker run --rm \
-v $(pwd)/config.toml:/app/config.toml:ro \
ghcr.io/benliao/bws:latest \
bws --config /app/config.toml --dry-run
Permission Issues:
# Fix volume permissions
sudo chown -R 1000:1000 static/ acme-challenges/
Port Conflicts:
# Check port usage
docker ps -a
netstat -tulpn | grep :8080
Debug Commands
# Run interactive shell
docker run -it --entrypoint /bin/sh ghcr.io/benliao/bws:latest
# Exec into running container
docker exec -it bws /bin/sh
# View container logs
docker logs -f bws
Security
Best Practices
- Use specific version tags (not
latest) in production - Mount configuration as read-only (
:ro) - Run as non-root user (default in BWS image)
- Use Docker secrets for sensitive data
Non-Root User
# BWS image runs as user 1000 by default
docker run -d \
--name bws-secure \
--user 1000:1000 \
-p 8080:8080 \
ghcr.io/benliao/bws:latest
Daemon Mode Configuration
BWS can run as a system daemon (background service) for production deployments, providing automatic startup, monitoring, and management capabilities.
Master-Worker Architecture
BWS uses a master-worker process model for production deployments:
Process Model
- Master Process: Manages configuration, handles signals, spawns workers
- Worker Processes: Handle HTTP traffic, SSL termination, proxying
- Hot Reload: Zero-downtime configuration updates via worker replacement
- Graceful Shutdown: Coordinated shutdown with connection draining
Signal Handling
BWS master process responds to standard Unix signals:
# Hot reload configuration (spawn new worker)
sudo kill -HUP $(pgrep -f "bws.*master")
# Graceful shutdown (stop all processes)
sudo kill -TERM $(pgrep -f "bws.*master")
# Check process tree
pstree -p $(pgrep -f "bws.*master")
Systemd Configuration
Service File Creation
Create a systemd service file for BWS:
# /etc/systemd/system/bws.service
[Unit]
Description=BWS Multi-Site Web Server
Documentation=https://github.com/yourusername/bws
After=network.target
Wants=network.target
[Service]
Type=simple
User=bws
Group=bws
WorkingDirectory=/opt/bws
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
TimeoutStartSec=60
TimeoutStopSec=30
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/bws /var/lib/bws
# Resource limits
LimitNOFILE=65536
LimitNPROC=4096
# Environment
Environment=RUST_LOG=info
Environment=BWS_CONFIG=/etc/bws/config.toml
Environment=BWS_LOG_FILE=/var/log/bws/bws.log
Environment=BWS_PID_FILE=/var/run/bws.pid
[Install]
WantedBy=multi-user.target
Installing the Service
# Copy service file
sudo cp bws.service /etc/systemd/system/
# Reload systemd
sudo systemctl daemon-reload
# Enable service (auto-start on boot)
sudo systemctl enable bws
# Start service
sudo systemctl start bws
# Check status
sudo systemctl status bws
Service Management
# Start the service
sudo systemctl start bws
# Stop the service
sudo systemctl stop bws
# Restart the service
sudo systemctl restart bws
# Reload configuration
sudo systemctl reload bws
# Check service status
sudo systemctl status bws
# View logs
sudo journalctl -u bws -f
# Check if service is enabled
sudo systemctl is-enabled bws
User and Directory Setup
Creating BWS User
# Create system user for BWS
sudo useradd -r -s /bin/false -d /opt/bws bws
# Create necessary directories
sudo mkdir -p /opt/bws
sudo mkdir -p /etc/bws
sudo mkdir -p /var/log/bws
sudo mkdir -p /var/lib/bws
# Set ownership
sudo chown -R bws:bws /opt/bws
sudo chown -R bws:bws /var/log/bws
sudo chown -R bws:bws /var/lib/bws
sudo chown root:bws /etc/bws
# Set permissions
sudo chmod 755 /opt/bws
sudo chmod 750 /etc/bws
sudo chmod 755 /var/log/bws
sudo chmod 755 /var/lib/bws
File Structure
/opt/bws/ # BWS home directory
โโโ static/ # Static files
โโโ sites/ # Multi-site configurations
โโโ bin/ # BWS binary (optional)
/etc/bws/ # Configuration directory
โโโ config.toml # Main configuration
โโโ sites/ # Site-specific configs
โโโ ssl/ # SSL certificates
/var/log/bws/ # Log directory
โโโ bws.log # Main log file
โโโ access.log # Access logs
โโโ error.log # Error logs
/var/lib/bws/ # Runtime data
โโโ cache/ # Cache files
โโโ temp/ # Temporary files
Configuration Files
Main Configuration
# /etc/bws/config.toml
[daemon]
user = "bws"
group = "bws"
pid_file = "/var/run/bws.pid"
working_directory = "/opt/bws"
[logging]
level = "info"
output = "file"
file_path = "/var/log/bws/bws.log"
max_size = "100MB"
max_files = 10
compress = true
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "/opt/bws/static"
[sites.headers]
"X-Served-By" = "BWS"
"Cache-Control" = "public, max-age=3600"
Environment Configuration
# /etc/bws/environment
BWS_CONFIG=/etc/bws/config.toml
BWS_LOG_FILE=/var/log/bws/bws.log
BWS_PID_FILE=/var/run/bws.pid
RUST_LOG=info
RUST_BACKTRACE=1
Process Management
Signal Handling
BWS master process responds to standard Unix signals:
# Hot reload configuration (spawn new worker)
sudo kill -HUP $(pgrep -f "bws.*master")
# Graceful shutdown (stop all processes)
sudo kill -TERM $(pgrep -f "bws.*master")
# Check process tree
pstree -p $(pgrep -f "bws.*master")
Master-Worker Operations
# View all BWS processes
ps aux | grep bws
# Check master process
pgrep -f "bws.*master"
# Monitor worker processes
pgrep -f "bws.*worker"
# Hot reload without downtime
systemctl reload bws
Process Monitoring
# Check if BWS is running
pgrep -f bws
# Monitor BWS process
ps aux | grep bws
# Check open files
sudo lsof -p $(cat /var/run/bws.pid)
# Monitor resource usage
top -p $(cat /var/run/bws.pid)
Log Management
Log Rotation Configuration
# /etc/logrotate.d/bws
/var/log/bws/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
copytruncate
postrotate
systemctl reload bws
endscript
}
Log Monitoring
# Follow main log
tail -f /var/log/bws/bws.log
# Follow with filtering
tail -f /var/log/bws/bws.log | grep ERROR
# Search logs
grep "error" /var/log/bws/bws.log
# Count log entries by level
grep -c "INFO\|WARN\|ERROR" /var/log/bws/bws.log
Monitoring and Health Checks
Health Check Script
#!/bin/bash
# /usr/local/bin/bws-health-check
BWS_PID_FILE="/var/run/bws.pid"
BWS_HEALTH_URL="http://localhost:8080/health"
# Check if PID file exists
if [ ! -f "$BWS_PID_FILE" ]; then
echo "ERROR: PID file not found"
exit 1
fi
# Check if process is running
PID=$(cat "$BWS_PID_FILE")
if ! kill -0 "$PID" 2>/dev/null; then
echo "ERROR: BWS process not running"
exit 1
fi
# Check health endpoint
if ! curl -f -s "$BWS_HEALTH_URL" > /dev/null; then
echo "ERROR: Health check failed"
exit 1
fi
echo "OK: BWS is healthy"
exit 0
Monitoring with Cron
# Add to crontab for user bws
*/5 * * * * /usr/local/bin/bws-health-check || /usr/bin/logger "BWS health check failed"
Systemd Timer for Health Checks
# /etc/systemd/system/bws-health.service
[Unit]
Description=BWS Health Check
After=bws.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/bws-health-check
User=bws
# /etc/systemd/system/bws-health.timer
[Unit]
Description=BWS Health Check Timer
Requires=bws-health.service
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
Auto-Recovery and Restart
Automatic Restart Configuration
# Enhanced systemd service with restart logic
[Service]
Type=simple
Restart=always
RestartSec=5
StartLimitInterval=60
StartLimitBurst=3
# Restart conditions
RestartPreventExitStatus=1 2 3 4 6 SIGTERM
Recovery Script
#!/bin/bash
# /usr/local/bin/bws-recovery
LOG_FILE="/var/log/bws/recovery.log"
PID_FILE="/var/run/bws.pid"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
# Check if BWS is running
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
if curl -f -s http://localhost:8080/health > /dev/null; then
log_message "BWS is healthy"
exit 0
fi
fi
log_message "BWS appears to be down, attempting restart"
# Stop any existing processes
systemctl stop bws
sleep 5
# Clean up PID file if exists
[ -f "$PID_FILE" ] && rm -f "$PID_FILE"
# Start BWS
if systemctl start bws; then
log_message "BWS restarted successfully"
exit 0
else
log_message "Failed to restart BWS"
exit 1
fi
Security Considerations
Service Security
# Enhanced security in systemd service
[Service]
# Run as non-root user
User=bws
Group=bws
# Security restrictions
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/log/bws /var/lib/bws
# Capability restrictions
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
# Network restrictions
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# File system restrictions
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
File Permissions
# Set secure permissions
chmod 600 /etc/bws/config.toml
chmod 755 /etc/bws
chmod 644 /usr/local/bin/bws
chmod 755 /usr/local/bin/bws
# Verify permissions
ls -la /etc/bws/
ls -la /var/log/bws/
ls -la /opt/bws/
Integration Examples
With Nginx
# /etc/nginx/sites-available/bws
upstream bws {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://bws;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /health {
proxy_pass http://bws/health;
access_log off;
}
}
With Load Balancer
# HAProxy configuration
global
daemon
maxconn 4096
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend web_frontend
bind *:80
default_backend bws_servers
backend bws_servers
balance roundrobin
option httpchk GET /health
server bws1 127.0.0.1:8080 check
server bws2 127.0.0.1:8081 check
Troubleshooting
Service Won't Start
# Check service status
systemctl status bws
# View detailed logs
journalctl -u bws -xe
# Check configuration
bws --config-check /etc/bws/config.toml
# Verify permissions
ls -la /etc/bws/config.toml
ls -la /usr/local/bin/bws
Permission Errors
# Fix ownership
sudo chown -R bws:bws /opt/bws /var/log/bws
# Fix permissions
sudo chmod 755 /opt/bws
sudo chmod 644 /etc/bws/config.toml
# Check SELinux (if applicable)
sestatus
setsebool -P httpd_can_network_connect 1
Performance Issues
# Check resource limits
systemctl show bws | grep Limit
# Monitor system resources
htop
iotop
netstat -tulpn
Best Practices
Configuration Management
- Store configurations in version control
- Use configuration templates for different environments
- Validate configurations before deployment
- Document all configuration changes
Monitoring
- Set up comprehensive logging
- Monitor service health continuously
- Configure alerting for service failures
- Regular log analysis and cleanup
Security
- Run with minimal privileges
- Regular security updates
- Secure file permissions
- Network security (firewall rules)
Maintenance
- Regular backup of configurations
- Monitor disk space for logs
- Plan for service updates
- Document operational procedures
Next Steps
- Configure Production Environment
- Set up Performance Monitoring
- Learn about Troubleshooting
Production Setup
Deploy BWS in production with security, performance, and reliability best practices.
Quick Production Setup
1. Install BWS
# Install from crates.io
cargo install bws-web-server
# Or use Docker
docker pull ghcr.io/benliao/bws:latest
2. Create Production Configuration
[server]
name = "BWS Production"
# Main HTTPS site
[[sites]]
name = "main"
hostname = "example.com"
port = 443
static_dir = "/var/www/html"
default = true
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
# Management API
[management]
enabled = true
port = 7654
api_key = "secure-random-key-here"
# Performance tuning
[performance]
worker_threads = 8
max_connections = 2000
# Security
[security]
hide_server_header = true
max_request_size = "50MB"
# Logging
[logging]
level = "info"
log_requests = true
3. System Setup
# Create user and directories
sudo useradd -r -s /bin/false bws
sudo mkdir -p /var/www/html /var/log/bws /etc/bws
sudo chown -R bws:bws /var/www /var/log/bws
# Copy configuration
sudo cp config.toml /etc/bws/
# Set permissions
sudo chmod 600 /etc/bws/config.toml
sudo chown bws:bws /etc/bws/config.toml
Systemd Service
Create Service File
# /etc/systemd/system/bws.service
[Unit]
Description=BWS Web Server
After=network.target
Wants=network.target
[Service]
Type=forking
User=bws
Group=bws
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml --daemon
ExecReload=/bin/kill -HUP $MAINPID
PIDFile=/var/run/bws.pid
Restart=always
RestartSec=5
LimitNOFILE=65535
# Security
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/www /var/log/bws /var/run
[Install]
WantedBy=multi-user.target
Service Management
# Enable and start service
sudo systemctl enable bws
sudo systemctl start bws
# Check status
sudo systemctl status bws
# View logs
sudo journalctl -u bws -f
# Reload configuration
sudo systemctl reload bws
Docker Production Deployment
Docker Compose
# docker-compose.prod.yml
version: '3.8'
services:
bws:
image: ghcr.io/benliao/bws:v0.3.5
container_name: bws-prod
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./config.toml:/app/config.toml:ro
- ./static:/app/static:ro
- ./acme-challenges:/app/acme-challenges
- ./logs:/app/logs
environment:
- BWS_LOG_LEVEL=info
networks:
- bws-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/api/health"]
interval: 30s
timeout: 10s
retries: 3
networks:
bws-network:
driver: bridge
Deploy with Docker
# Start production stack
docker-compose -f docker-compose.prod.yml up -d
# View logs
docker-compose -f docker-compose.prod.yml logs -f
# Update deployment
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d
Reverse Proxy Setup
Nginx Frontend (Optional)
# /etc/nginx/sites-available/bws
upstream bws_backend {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://bws_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Security
Firewall Configuration
# UFW setup
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
# Restrict management API
sudo ufw allow from 127.0.0.1 to any port 7654
SSL/TLS Security
# Strong SSL configuration
[sites.ssl]
enabled = true
auto_cert = true
min_tls_version = "1.2"
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains; preload"
"Content-Security-Policy" = "default-src 'self'"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"Referrer-Policy" = "strict-origin-when-cross-origin"
Monitoring
Health Checks
# Basic health check
curl -f http://localhost/api/health
# Detailed health information
curl http://localhost/api/health/detailed
# Check SSL certificate
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
Log Monitoring
# Monitor access logs
tail -f /var/log/bws/access.log
# Monitor error logs
tail -f /var/log/bws/error.log
# System resource monitoring
htop
iostat -x 1
Alerting Setup
# Check certificate expiration
#!/bin/bash
# check-ssl.sh
DOMAIN="example.com"
EXPIRY=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_DATE=$(date -d "$EXPIRY" +%s)
CURRENT_DATE=$(date +%s)
DAYS_UNTIL_EXPIRY=$(( ($EXPIRY_DATE - $CURRENT_DATE) / 86400 ))
if [ $DAYS_UNTIL_EXPIRY -lt 30 ]; then
echo "SSL certificate for $DOMAIN expires in $DAYS_UNTIL_EXPIRY days!"
# Send alert (email, Slack, etc.)
fi
Performance Tuning
System Limits
# /etc/security/limits.conf
bws soft nofile 65535
bws hard nofile 65535
# /etc/sysctl.conf
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 5000
BWS Configuration
[performance]
worker_threads = 16 # 2x CPU cores
max_connections = 10000 # Per worker
keep_alive_timeout = 75 # Seconds
request_timeout = 30 # Seconds
max_header_size = "16KB"
Backup and Recovery
Configuration Backup
# Backup script
#!/bin/bash
BACKUP_DIR="/backups/bws/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR
# Backup configuration
cp /etc/bws/config.toml $BACKUP_DIR/
cp -r /var/www $BACKUP_DIR/
cp -r /etc/letsencrypt $BACKUP_DIR/
# Create archive
tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR
rm -rf $BACKUP_DIR
Recovery Procedure
# Restore from backup
tar -xzf /backups/bws/20250827.tar.gz -C /
# Restore permissions
sudo chown -R bws:bws /var/www /etc/bws
sudo chmod 600 /etc/bws/config.toml
# Restart service
sudo systemctl restart bws
Troubleshooting
Common Issues
# Check BWS status
sudo systemctl status bws
# Validate configuration
bws --config /etc/bws/config.toml --dry-run
# Check logs
sudo journalctl -u bws --since "1 hour ago"
# Test configuration reload
curl -X POST http://127.0.0.1:7654/api/config/reload \
-H "X-API-Key: your-api-key"
# Check process tree
pstree -p $(pgrep -f bws)
Performance Debugging
# Check resource usage
top -p $(pgrep bws)
# Network connections
ss -tulpn | grep bws
# File descriptors
lsof -p $(pgrep bws) | wc -l
Updates and Maintenance
Rolling Updates
# Download new version
cargo install bws-web-server --force
# Validate new binary
bws --version
bws --config /etc/bws/config.toml --dry-run
# Hot reload (zero downtime)
sudo systemctl reload bws
# Or restart if needed
sudo systemctl restart bws
Maintenance Window
# Graceful shutdown
sudo systemctl stop bws
# Perform maintenance
# - Update system packages
# - Update BWS configuration
# - Clean log files
# Start service
sudo systemctl start bws
# Verify operation
curl -f http://localhost/api/health
Performance Tuning
This guide covers optimizing BWS for maximum performance, throughput, and efficiency in production environments.
Performance Overview
BWS is built on Pingora, providing excellent performance characteristics:
- High-performance async I/O
- Minimal memory footprint
- Efficient connection handling
- Built-in caching capabilities
- Horizontal scaling support
System-Level Optimization
Operating System Tuning
Network Stack Optimization
# /etc/sysctl.d/99-bws-performance.conf
# TCP/IP stack tuning
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 15
# Buffer sizes
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Connection tracking
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 300
# Apply settings
sysctl -p /etc/sysctl.d/99-bws-performance.conf
File Descriptor Limits
# /etc/security/limits.d/bws-performance.conf
bws soft nofile 1048576
bws hard nofile 1048576
bws soft nproc 32768
bws hard nproc 32768
# For systemd services
# /etc/systemd/system/bws.service.d/limits.conf
[Service]
LimitNOFILE=1048576
LimitNPROC=32768
CPU and Memory Optimization
# CPU governor for performance
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# Disable swap for consistent performance
swapoff -a
# Comment out swap in /etc/fstab
# NUMA optimization (for multi-socket systems)
echo 0 > /proc/sys/kernel/numa_balancing
Storage Optimization
File System Tuning
# Mount options for static files (add to /etc/fstab)
/dev/sdb1 /opt/bws/static ext4 defaults,noatime,nodiratime,data=writeback 0 0
# For high-performance scenarios
/dev/nvme0n1 /opt/bws/cache xfs defaults,noatime,largeio,inode64 0 0
# Temporary files in memory
tmpfs /tmp tmpfs defaults,size=2G,mode=1777 0 0
tmpfs /var/tmp tmpfs defaults,size=1G,mode=1777 0 0
I/O Scheduler Optimization
# For SSDs
echo noop > /sys/block/sda/queue/scheduler
# For HDDs
echo deadline > /sys/block/sda/queue/scheduler
# Make persistent (add to /etc/rc.local or systemd service)
echo 'noop' > /sys/block/sda/queue/scheduler
BWS Configuration Optimization
Performance-Focused Configuration
# /etc/bws/performance.toml
[performance]
# Worker thread configuration
worker_threads = 16 # 2x CPU cores for I/O bound workloads
max_blocking_threads = 512
# Connection settings
max_connections = 100000
keep_alive_timeout = 60
request_timeout = 30
response_timeout = 30
# Buffer sizes
read_buffer_size = "64KB"
write_buffer_size = "64KB"
max_request_size = "10MB"
# Connection pooling
connection_pool_size = 1000
connection_pool_idle_timeout = 300
[caching]
enabled = true
max_memory = "1GB"
ttl_default = 3600
ttl_static = 86400
[compression]
enabled = true
level = 6 # Balance between CPU and compression ratio
min_size = 1024 # Don't compress small files
[[sites]]
name = "high-performance"
hostname = "0.0.0.0"
port = 8080
static_dir = "/opt/bws/static"
# Optimized headers for caching
[sites.headers]
"Cache-Control" = "public, max-age=31536000, immutable"
"Vary" = "Accept-Encoding"
"X-Content-Type-Options" = "nosniff"
Memory Management
[memory]
# Garbage collection tuning
gc_threshold = 0.8 # Trigger GC at 80% memory usage
max_heap_size = "4GB"
# Buffer pool settings
buffer_pool_size = "512MB"
buffer_pool_max_buffers = 10000
# Static file caching
file_cache_size = "2GB"
file_cache_max_files = 100000
CPU Optimization
[cpu]
# Thread affinity (if supported)
pin_threads = true
cpu_affinity = [0, 1, 2, 3, 4, 5, 6, 7] # Pin to specific cores
# Async runtime tuning
io_uring = true # Use io_uring if available (Linux 5.1+)
event_loop_threads = 4
Load Testing and Benchmarking
Benchmarking Tools
wrk - HTTP Benchmarking
# Install wrk
sudo apt install wrk
# Basic load test
wrk -t12 -c400 -d30s --latency http://localhost:8080/
# Custom script for complex scenarios
cat > test-script.lua << 'EOF'
wrk.method = "GET"
wrk.headers["User-Agent"] = "wrk-benchmark"
request = function()
local path = "/static/file" .. math.random(1, 1000) .. ".jpg"
return wrk.format(nil, path)
end
EOF
wrk -t12 -c400 -d30s -s test-script.lua http://localhost:8080/
Apache Bench (ab)
# Simple test
ab -n 10000 -c 100 http://localhost:8080/
# With keep-alive
ab -n 10000 -c 100 -k http://localhost:8080/
# POST requests
ab -n 1000 -c 10 -p post_data.json -T application/json http://localhost:8080/api/test
hey - Modern Load Testing
# Install hey
go install github.com/rakyll/hey@latest
# Basic test
hey -n 10000 -c 100 http://localhost:8080/
# With custom headers
hey -n 10000 -c 100 -H "Accept: application/json" http://localhost:8080/api/health
# Rate limited test
hey -n 10000 -q 100 http://localhost:8080/
Performance Testing Strategy
Baseline Testing
#!/bin/bash
# performance-baseline.sh
SERVER_URL="http://localhost:8080"
RESULTS_DIR="/tmp/performance-results"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$RESULTS_DIR"
echo "Running BWS performance baseline tests - $DATE"
# Test 1: Static file serving
echo "Testing static file serving..."
wrk -t4 -c50 -d60s --latency "$SERVER_URL/static/test.html" > "$RESULTS_DIR/static-$DATE.log"
# Test 2: API endpoints
echo "Testing API endpoints..."
wrk -t4 -c50 -d60s --latency "$SERVER_URL/health" > "$RESULTS_DIR/api-$DATE.log"
# Test 3: High concurrency
echo "Testing high concurrency..."
wrk -t12 -c1000 -d60s --latency "$SERVER_URL/" > "$RESULTS_DIR/concurrency-$DATE.log"
# Test 4: Sustained load
echo "Testing sustained load..."
wrk -t8 -c200 -d300s --latency "$SERVER_URL/" > "$RESULTS_DIR/sustained-$DATE.log"
echo "Performance tests completed. Results in $RESULTS_DIR"
Stress Testing
#!/bin/bash
# stress-test.sh
# Gradually increase load
for connections in 100 500 1000 2000 5000; do
echo "Testing with $connections connections..."
wrk -t12 -c$connections -d30s --latency http://localhost:8080/ > "stress-$connections.log"
# Check if server is still responsive
if ! curl -f -s http://localhost:8080/health > /dev/null; then
echo "Server failed at $connections connections"
break
fi
# Cool down period
sleep 10
done
Performance Monitoring
Real-time Monitoring Script
#!/bin/bash
# monitor-performance.sh
LOG_FILE="/var/log/bws/performance.log"
INTERVAL=5
log_metrics() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
local memory_usage=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}')
local connections=$(ss -tuln | grep :8080 | wc -l)
local load_avg=$(uptime | awk -F'load average:' '{print $2}' | cut -d',' -f1 | xargs)
echo "$timestamp,CPU:$cpu_usage%,Memory:$memory_usage%,Connections:$connections,Load:$load_avg" >> "$LOG_FILE"
}
echo "Starting performance monitoring (interval: ${INTERVAL}s)"
echo "Timestamp,CPU,Memory,Connections,LoadAvg" > "$LOG_FILE"
while true; do
log_metrics
sleep $INTERVAL
done
Performance Dashboard Script
#!/bin/bash
# performance-dashboard.sh
# Real-time performance dashboard
watch -n 1 '
echo "=== BWS Performance Dashboard ==="
echo "Time: $(date)"
echo ""
echo "=== System Resources ==="
echo "CPU Usage: $(top -bn1 | grep "Cpu(s)" | awk "{print \$2}")"
echo "Memory: $(free -h | grep Mem | awk "{printf \"Used: %s/%s (%.1f%%)\", \$3, \$2, \$3/\$2*100}")"
echo "Load Average: $(uptime | awk -F"load average:" "{print \$2}")"
echo ""
echo "=== Network ==="
echo "Active Connections: $(ss -tuln | grep :8080 | wc -l)"
echo "TCP Connections: $(ss -s | grep TCP | head -1)"
echo ""
echo "=== BWS Process ==="
BWS_PID=$(pgrep bws)
if [ ! -z "$BWS_PID" ]; then
echo "Process ID: $BWS_PID"
echo "Memory Usage: $(ps -o pid,ppid,cmd,%mem,%cpu -p $BWS_PID | tail -1)"
echo "File Descriptors: $(lsof -p $BWS_PID | wc -l)"
else
echo "BWS process not found"
fi
'
Optimization Strategies
Static File Optimization
File Compression
#!/bin/bash
# precompress-static-files.sh
STATIC_DIR="/opt/bws/static"
# Pre-compress static files
find "$STATIC_DIR" -type f \( -name "*.css" -o -name "*.js" -o -name "*.html" -o -name "*.svg" \) | while read file; do
# Gzip compression
if [ ! -f "$file.gz" ] || [ "$file" -nt "$file.gz" ]; then
gzip -k -9 "$file"
echo "Compressed: $file"
fi
# Brotli compression (if available)
if command -v brotli > /dev/null; then
if [ ! -f "$file.br" ] || [ "$file" -nt "$file.br" ]; then
brotli -k -q 11 "$file"
echo "Brotli compressed: $file"
fi
fi
done
# Optimize images
find "$STATIC_DIR" -name "*.jpg" -o -name "*.jpeg" | while read file; do
if command -v jpegoptim > /dev/null; then
jpegoptim --strip-all --max=85 "$file"
fi
done
find "$STATIC_DIR" -name "*.png" | while read file; do
if command -v optipng > /dev/null; then
optipng -o2 "$file"
fi
done
CDN Integration
# Configure BWS for CDN usage
[[sites]]
name = "cdn-optimized"
hostname = "localhost"
port = 8080
static_dir = "/opt/bws/static"
[sites.headers]
"Cache-Control" = "public, max-age=31536000, immutable"
"Access-Control-Allow-Origin" = "*"
"Vary" = "Accept-Encoding"
"X-CDN-Cache" = "MISS"
# Separate configuration for CDN edge
[[sites]]
name = "edge"
hostname = "edge.example.com"
port = 8081
static_dir = "/opt/bws/edge-cache"
[sites.headers]
"Cache-Control" = "public, max-age=604800" # 1 week for edge cache
"X-CDN-Cache" = "HIT"
Database and Cache Optimization
Redis Integration (if using caching)
# Redis performance tuning
# /etc/redis/redis.conf
# Memory settings
maxmemory 2gb
maxmemory-policy allkeys-lru
# Persistence settings (adjust based on needs)
save 900 1
save 300 10
save 60 10000
# Network settings
tcp-keepalive 60
timeout 0
# Performance settings
tcp-backlog 511
databases 16
Memcached Configuration
# /etc/memcached.conf
-m 1024 # 1GB memory
-c 1024 # Max connections
-t 4 # Number of threads
-l 127.0.0.1 # Listen address
-p 11211 # Port
Reverse Proxy Optimization
Nginx Performance Configuration
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
worker_rlimit_nofile 100000;
events {
worker_connections 4000;
use epoll;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
keepalive_requests 100000;
reset_timedout_connection on;
client_body_timeout 10;
send_timeout 2;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# Buffer sizes
client_max_body_size 10m;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
output_buffers 1 32k;
postpone_output 1460;
# Proxy settings
proxy_buffering on;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
upstream bws_backend {
least_conn;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
keepalive 300;
}
server {
listen 80 default_server;
location / {
proxy_pass http://bws_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Caching
proxy_cache_valid 200 1h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
}
}
}
Performance Monitoring and Analysis
Key Performance Metrics
Response Time Monitoring
#!/bin/bash
# response-time-monitor.sh
URL="http://localhost:8080"
LOG_FILE="/var/log/bws/response-times.log"
while true; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
RESPONSE_TIME=$(curl -o /dev/null -s -w "%{time_total}" "$URL/health")
RESPONSE_CODE=$(curl -o /dev/null -s -w "%{http_code}" "$URL/health")
echo "$TIMESTAMP,$RESPONSE_TIME,$RESPONSE_CODE" >> "$LOG_FILE"
sleep 1
done
Throughput Measurement
#!/bin/bash
# throughput-monitor.sh
# Monitor requests per second
LOG_FILE="/var/log/bws/throughput.log"
ACCESS_LOG="/var/log/nginx/access.log"
while true; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
RPS=$(tail -60 "$ACCESS_LOG" | grep "$(date '+%d/%b/%Y:%H:%M')" | wc -l)
echo "$TIMESTAMP,$RPS" >> "$LOG_FILE"
sleep 60
done
Performance Analysis Tools
Log Analysis
#!/bin/bash
# analyze-performance.sh
LOG_FILE="/var/log/nginx/access.log"
OUTPUT_DIR="/tmp/performance-analysis"
DATE=$(date +%Y%m%d)
mkdir -p "$OUTPUT_DIR"
echo "Analyzing performance for $DATE"
# Top requested URLs
echo "=== Top Requested URLs ===" > "$OUTPUT_DIR/top-urls-$DATE.txt"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -20 >> "$OUTPUT_DIR/top-urls-$DATE.txt"
# Response codes distribution
echo "=== Response Codes ===" > "$OUTPUT_DIR/response-codes-$DATE.txt"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn >> "$OUTPUT_DIR/response-codes-$DATE.txt"
# Response time analysis (if logged)
echo "=== Response Time Analysis ===" > "$OUTPUT_DIR/response-times-$DATE.txt"
awk '{print $NF}' "$LOG_FILE" | grep -E '^[0-9]+\.[0-9]+$' | \
awk '{
sum += $1
count++
if ($1 > max) max = $1
if (min == 0 || $1 < min) min = $1
}
END {
print "Average:", sum/count
print "Min:", min
print "Max:", max
}' >> "$OUTPUT_DIR/response-times-$DATE.txt"
# Hourly request distribution
echo "=== Hourly Request Distribution ===" > "$OUTPUT_DIR/hourly-requests-$DATE.txt"
awk '{print substr($4, 14, 2)}' "$LOG_FILE" | sort | uniq -c >> "$OUTPUT_DIR/hourly-requests-$DATE.txt"
echo "Analysis complete. Results in $OUTPUT_DIR"
Resource Usage Trends
#!/bin/bash
# resource-trends.sh
DURATION=${1:-3600} # Default 1 hour
INTERVAL=10
SAMPLES=$((DURATION / INTERVAL))
echo "Collecting resource usage data for $DURATION seconds..."
echo "timestamp,cpu_percent,memory_mb,connections,load_avg" > resource-usage.csv
for i in $(seq 1 $SAMPLES); do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
CPU_PERCENT=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
MEMORY_MB=$(ps -o pid,rss -p $(pgrep bws) | tail -1 | awk '{print $2/1024}')
CONNECTIONS=$(ss -tuln | grep :8080 | wc -l)
LOAD_AVG=$(uptime | awk -F'load average:' '{print $2}' | cut -d',' -f1 | xargs)
echo "$TIMESTAMP,$CPU_PERCENT,$MEMORY_MB,$CONNECTIONS,$LOAD_AVG" >> resource-usage.csv
sleep $INTERVAL
done
echo "Resource usage data collected in resource-usage.csv"
Optimization Recommendations
Hardware Recommendations
CPU Optimization
- Use modern CPUs with high single-thread performance
- Consider NUMA topology for multi-socket systems
- Enable CPU turbo boost for peak performance
- Pin BWS processes to specific CPU cores for consistency
Memory Optimization
- Use ECC RAM for data integrity
- Ensure sufficient RAM for file caching
- Consider NUMA-aware memory allocation
- Monitor for memory leaks and fragmentation
Storage Optimization
- Use NVMe SSDs for static file storage
- Implement RAID for redundancy without performance penalty
- Consider separate storage for logs and static files
- Use tmpfs for temporary files and cache
Network Optimization
- Use 10Gbps+ network interfaces for high-traffic scenarios
- Implement network bonding for redundancy
- Optimize network driver settings
- Consider SR-IOV for virtualized environments
Application-Level Optimization
Code Optimization
#![allow(unused)] fn main() { // Example optimizations in BWS configuration use pingora::prelude::*; use tokio::runtime::Builder; // Custom runtime configuration let runtime = Builder::new_multi_thread() .worker_threads(num_cpus::get() * 2) .max_blocking_threads(512) .thread_keep_alive(Duration::from_secs(60)) .enable_all() .build() .unwrap(); // Connection pooling optimization let pool_config = ConnectionPoolConfig { max_connections_per_host: 100, idle_timeout: Duration::from_secs(300), connect_timeout: Duration::from_secs(10), }; }
Memory Pool Configuration
[memory_pool]
small_buffer_size = "4KB"
medium_buffer_size = "64KB"
large_buffer_size = "1MB"
pool_size = 1000
Scaling Strategies
Horizontal Scaling
# Kubernetes deployment example
apiVersion: apps/v1
kind: Deployment
metadata:
name: bws-deployment
spec:
replicas: 6
selector:
matchLabels:
app: bws
template:
metadata:
labels:
app: bws
spec:
containers:
- name: bws
image: ghcr.io/yourusername/bws:latest
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: bws-service
spec:
selector:
app: bws
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
Vertical Scaling
# Automatic resource scaling script
#!/bin/bash
# auto-scale.sh
CPU_THRESHOLD=80
MEMORY_THRESHOLD=80
CHECK_INTERVAL=60
while true; do
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
MEMORY_USAGE=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
if (( $(echo "$CPU_USAGE > $CPU_THRESHOLD" | bc -l) )); then
echo "High CPU usage detected: $CPU_USAGE%"
# Scale up logic here
fi
if (( MEMORY_USAGE > MEMORY_THRESHOLD )); then
echo "High memory usage detected: $MEMORY_USAGE%"
# Scale up logic here
fi
sleep $CHECK_INTERVAL
done
Performance Best Practices
Configuration Best Practices
- Right-size worker threads: 2x CPU cores for I/O-bound workloads
- Optimize buffer sizes: Match your typical request/response sizes
- Enable compression: For text-based content
- Use connection pooling: Reuse connections for better performance
- Configure appropriate timeouts: Balance responsiveness and resource usage
Monitoring Best Practices
- Monitor key metrics: Response time, throughput, error rate, resource usage
- Set up alerting: For performance degradation
- Regular performance testing: Catch regressions early
- Capacity planning: Monitor trends and plan for growth
- Document baselines: Know your normal performance characteristics
Deployment Best Practices
- Load testing: Test under realistic load before production
- Gradual rollouts: Use blue-green or canary deployments
- Performance regression testing: Automated checks for performance changes
- Resource monitoring: Continuous monitoring of system resources
- Regular optimization: Periodic performance reviews and tuning
Next Steps
- Review Configuration Schema for advanced tuning options
- Check Troubleshooting for performance issues
- Learn about Production Setup for deployment strategies
REST API
BWS provides a comprehensive REST API for monitoring, management, and configuration validation.
Endpoints
Health Check
GET /api/health
Returns the basic server health status.
Response:
{
"status": "healthy",
"timestamp": "2025-08-26T12:00:00Z",
"version": "0.3.4",
"uptime_seconds": 3600
}
Example:
curl http://localhost:8080/api/health
Detailed Health Information
GET /api/health/detailed
Returns comprehensive server information including system details.
Response:
{
"status": "healthy",
"timestamp": "2025-08-26T12:00:00Z",
"version": "0.3.4",
"uptime_seconds": 3600,
"system": {
"platform": "linux",
"memory_usage": "45MB",
"cpu_usage": "2.1%"
},
"sites": {
"total": 4,
"active": 4
},
"configuration": {
"last_reload": "2025-08-26T11:30:00Z",
"config_file": "/app/config.toml"
}
}
Example:
curl http://localhost:8080/api/health/detailed | jq
Sites Information
GET /api/sites
Returns information about all configured sites.
Response:
{
"sites": [
{
"name": "main",
"hostname": "localhost",
"port": 8080,
"static_dir": "static",
"default": true,
"headers": {
"X-Site-Name": "BWS Main Site",
"X-Powered-By": "BWS/1.0"
}
}
],
"total_sites": 1
}
Example:
curl http://localhost:8080/api/sites | jq
Configuration Reload
POST /api/reload
Triggers a hot reload of the configuration file without restarting the server.
Request:
curl -X POST http://localhost:8080/api/reload
Success Response (200 OK):
{
"status": "success",
"message": "Configuration reloaded successfully",
"timestamp": "2025-08-26T12:00:00Z",
"sites_reloaded": 4
}
Error Response (400 Bad Request):
{
"status": "error",
"message": "Configuration validation failed: Missing required field 'hostname' for site 'main'",
"timestamp": "2025-08-26T12:00:00Z"
}
Note: The reload endpoint validates the new configuration before applying it. If validation fails, the existing configuration remains active.
Response Headers
All API responses include these headers:
Content-Type: application/jsonX-Powered-By: BWS/0.3.4- Site-specific custom headers (if configured)
Error Responses
API endpoints return standard HTTP status codes:
200 OK- Successful request400 Bad Request- Invalid request or configuration error404 Not Found- Endpoint not found500 Internal Server Error- Server error
Error Format:
{
"error": "Not Found",
"message": "The requested endpoint does not exist",
"available_endpoints": ["/", "/api/health", "/api/sites"]
}
Management API
BWS provides a separate, secure Management API for administrative operations. This API runs on a dedicated port (default: 7654) and is restricted to localhost access only.
Security
The Management API implements security-first design:
- Localhost Only: Always binds to
127.0.0.1- no external access - IP Validation: Double-checks request origin is localhost
- Optional API Key: Additional authentication layer
- Audit Logging: All operations logged with client IP
Base URL
The Management API runs on a separate port from the main web server:
http://127.0.0.1:7654
Authentication
Authentication is optional but recommended for production:
# Without API key (localhost only)
curl -X POST http://127.0.0.1:7654/api/config/reload
# With API key
curl -X POST http://127.0.0.1:7654/api/config/reload \
-H "X-API-Key: your-secure-api-key"
Configuration Reload
POST /api/config/reload
Reloads the server configuration from the configuration file.
Headers:
X-API-Key(optional): API key for authentication
Response (Success):
{
"status": "success",
"message": "Configuration reloaded successfully",
"config_path": "config.toml",
"timestamp": "2025-08-26T15:27:07Z",
"note": "Changes will apply to new connections"
}
Response (Error):
{
"error": "Configuration reload failed",
"details": "Invalid TOML syntax at line 15"
}
Example:
# Basic reload
curl -X POST http://127.0.0.1:7654/api/config/reload
# With API key
curl -X POST http://127.0.0.1:7654/api/config/reload \
-H "X-API-Key: your-secure-api-key"
Management API Errors
The Management API returns specific error codes:
401 Unauthorized- Invalid or missing API key403 Forbidden- Request not from localhost404 Not Found- Endpoint does not exist500 Internal Server Error- Configuration reload failed
Security Error Example:
{
"error": "Access denied: localhost only"
}
Authentication Error Example:
{
"error": "Unauthorized: invalid API key"
}
Configuration Schema
This document provides a complete reference for all BWS configuration options in the config.toml file.
Configuration File Structure
BWS uses TOML format for configuration. The basic structure is:
# Global settings
[daemon]
# Daemon configuration
[logging]
# Logging configuration
[performance]
# Performance tuning
[monitoring]
# Health monitoring
# Site definitions
[[sites]]
# First site configuration
[[sites]]
# Second site configuration
Global Configuration Sections
Daemon Configuration
Controls how BWS runs as a daemon process.
[daemon]
user = "bws" # User to run as (string)
group = "bws" # Group to run as (string)
pid_file = "/var/run/bws.pid" # PID file location (string)
working_directory = "/opt/bws" # Working directory (string)
daemonize = true # Run as daemon (boolean)
Parameters:
user(string, optional): System user to run BWS as. Default: current usergroup(string, optional): System group to run BWS as. Default: current user's grouppid_file(string, optional): Path to store process ID file. Default: no PID fileworking_directory(string, optional): Change to this directory on startupdaemonize(boolean, optional): Fork and run in background. Default:false
Management API Configuration
Controls the secure Management API for administrative operations.
[management]
enabled = true # Enable Management API (boolean)
host = "127.0.0.1" # Host to bind to (string)
port = 7654 # Port number (integer)
api_key = "your-secure-api-key" # API key for authentication (string)
Parameters:
enabled(boolean, optional): Enable the Management API service. Default:falsehost(string, optional): Host address to bind to. Always127.0.0.1for security. Default:"127.0.0.1"port(integer, optional): Port number for the Management API. Default:7654api_key(string, optional): API key for authentication. If not set, no authentication required. Default:null
Security Features:
- Management API always binds to localhost only for security
- IP address validation ensures requests come from localhost
- Optional API key authentication for additional security
- All management operations are logged with client IP
Available Endpoints:
POST /api/config/reload: Reload server configuration
Logging Configuration
Controls logging behavior and output.
[logging]
level = "info" # Log level (string)
output = "stdout" # Output destination (string)
format = "json" # Log format (string)
file_path = "/var/log/bws/bws.log" # Log file path (string)
max_size = "100MB" # Maximum log file size (string)
max_files = 10 # Number of log files to keep (integer)
compress = true # Compress rotated logs (boolean)
include_fields = [ # Fields to include in logs (array)
"timestamp",
"level",
"message",
"request_id"
]
Parameters:
level(string, optional): Log level. Values:trace,debug,info,warn,error. Default:infooutput(string, optional): Where to send logs. Values:stdout,stderr,file. Default:stdoutformat(string, optional): Log format. Values:text,json. Default:textfile_path(string, required if output="file"): Path to log filemax_size(string, optional): Maximum size before rotation. Examples:10MB,1GB. Default:100MBmax_files(integer, optional): Number of rotated files to keep. Default:10compress(boolean, optional): Compress rotated log files. Default:trueinclude_fields(array, optional): Fields to include in structured logs
Performance Configuration
Tuning parameters for performance optimization.
[performance]
worker_threads = 8 # Number of worker threads (integer)
max_blocking_threads = 512 # Max blocking threads (integer)
max_connections = 10000 # Maximum concurrent connections (integer)
keep_alive_timeout = 60 # Keep-alive timeout in seconds (integer)
request_timeout = 30 # Request timeout in seconds (integer)
response_timeout = 30 # Response timeout in seconds (integer)
read_buffer_size = "64KB" # Read buffer size (string)
write_buffer_size = "64KB" # Write buffer size (string)
max_request_size = "10MB" # Maximum request size (string)
connection_pool_size = 1000 # Connection pool size (integer)
connection_pool_idle_timeout = 300 # Pool idle timeout in seconds (integer)
Parameters:
worker_threads(integer, optional): Number of async worker threads. Default: number of CPU coresmax_blocking_threads(integer, optional): Maximum blocking threads for file I/O. Default:512max_connections(integer, optional): Maximum concurrent connections. Default:10000keep_alive_timeout(integer, optional): HTTP keep-alive timeout in seconds. Default:60request_timeout(integer, optional): Request processing timeout in seconds. Default:30response_timeout(integer, optional): Response sending timeout in seconds. Default:30read_buffer_size(string, optional): Buffer size for reading requests. Default:8KBwrite_buffer_size(string, optional): Buffer size for writing responses. Default:8KBmax_request_size(string, optional): Maximum allowed request size. Default:1MBconnection_pool_size(integer, optional): Size of connection pool. Default:100connection_pool_idle_timeout(integer, optional): Idle timeout for pooled connections. Default:300
Monitoring Configuration
Health monitoring and metrics collection.
[monitoring]
enabled = true # Enable monitoring (boolean)
health_endpoint = "/health" # Health check endpoint (string)
detailed_endpoint = "/health/detailed" # Detailed health endpoint (string)
metrics_endpoint = "/metrics" # Metrics endpoint for Prometheus (string)
[monitoring.checks]
disk_threshold = 90 # Disk usage alert threshold (integer)
memory_threshold = 80 # Memory usage alert threshold (integer)
response_time_threshold = 1000 # Response time alert threshold in ms (integer)
[monitoring.prometheus]
enabled = true # Enable Prometheus metrics (boolean)
endpoint = "/metrics" # Metrics endpoint path (string)
port = 9090 # Metrics server port (integer)
Parameters:
enabled(boolean, optional): Enable health monitoring. Default:truehealth_endpoint(string, optional): Path for basic health checks. Default:/healthdetailed_endpoint(string, optional): Path for detailed health info. Default:/health/detailedmetrics_endpoint(string, optional): Path for Prometheus metrics. Default:/metrics
Monitoring Checks:
disk_threshold(integer, optional): Alert when disk usage exceeds this percentage. Default:90memory_threshold(integer, optional): Alert when memory usage exceeds this percentage. Default:80response_time_threshold(integer, optional): Alert when response time exceeds this value in milliseconds. Default:1000
Prometheus Integration:
enabled(boolean, optional): Enable Prometheus metrics export. Default:falseendpoint(string, optional): Metrics endpoint path. Default:/metricsport(integer, optional): Port for metrics server. Default: same as main site
Caching Configuration
Configure caching behavior for static files.
[caching]
enabled = true # Enable caching (boolean)
max_memory = "1GB" # Maximum memory for cache (string)
ttl_default = 3600 # Default TTL in seconds (integer)
ttl_static = 86400 # TTL for static files (integer)
max_file_size = "10MB" # Maximum cacheable file size (string)
cache_control_override = false # Override Cache-Control headers (boolean)
Parameters:
enabled(boolean, optional): Enable response caching. Default:falsemax_memory(string, optional): Maximum memory to use for cache. Default:100MBttl_default(integer, optional): Default cache TTL in seconds. Default:3600ttl_static(integer, optional): TTL for static files in seconds. Default:86400max_file_size(string, optional): Maximum size of files to cache. Default:1MBcache_control_override(boolean, optional): Override existing Cache-Control headers. Default:false
Compression Configuration
Configure response compression.
[compression]
enabled = true # Enable compression (boolean)
level = 6 # Compression level 1-9 (integer)
min_size = 1024 # Minimum size to compress (integer)
algorithms = ["gzip", "deflate"] # Compression algorithms (array)
types = [ # MIME types to compress (array)
"text/html",
"text/css",
"application/javascript",
"application/json"
]
Parameters:
enabled(boolean, optional): Enable response compression. Default:falselevel(integer, optional): Compression level (1-9, higher = better compression). Default:6min_size(integer, optional): Minimum response size to compress in bytes. Default:1024algorithms(array, optional): Supported compression algorithms. Default:["gzip"]types(array, optional): MIME types to compress. Default: common text types
Site Configuration
Each [[sites]] section defines a virtual host or site.
Basic Site Configuration
[[sites]]
name = "example" # Site identifier (string)
hostname = "localhost" # Hostname to bind to (string)
port = 8080 # Port to listen on (integer)
static_dir = "static" # Directory for static files (string)
index_file = "index.html" # Default index file (string)
Required Parameters:
name(string): Unique identifier for this sitehostname(string): Hostname or IP address to bind toport(integer): TCP port number to listen onstatic_dir(string): Path to directory containing static files
Optional Parameters:
index_file(string, optional): Default file to serve for directory requests. Default:index.html
Advanced Site Configuration
[[sites]]
name = "advanced"
hostname = "example.com"
port = 8080
static_dir = "/var/www/example"
index_file = "index.html"
enable_directory_listing = false # Allow directory browsing (boolean)
follow_symlinks = false # Follow symbolic links (boolean)
case_sensitive = true # Case-sensitive file matching (boolean)
max_age = 3600 # Default cache max-age (integer)
cors_enabled = true # Enable CORS headers (boolean)
Additional Parameters:
enable_directory_listing(boolean, optional): Allow browsing directories without index files. Default:falsefollow_symlinks(boolean, optional): Follow symbolic links when serving files. Default:falsecase_sensitive(boolean, optional): Case-sensitive URL matching. Default:truemax_age(integer, optional): Default Cache-Control max-age in seconds. Default:3600cors_enabled(boolean, optional): Enable CORS headers for cross-origin requests. Default:false
Site Headers Configuration
Custom HTTP headers for responses from a site.
[[sites]]
name = "example"
hostname = "localhost"
port = 8080
static_dir = "static"
[sites.headers]
"Cache-Control" = "public, max-age=3600"
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "DENY"
"X-XSS-Protection" = "1; mode=block"
"Strict-Transport-Security" = "max-age=31536000"
"Content-Security-Policy" = "default-src 'self'"
"Referrer-Policy" = "strict-origin-when-cross-origin"
"Access-Control-Allow-Origin" = "*"
"Access-Control-Allow-Methods" = "GET, POST, PUT, DELETE"
"X-Custom-Header" = "custom-value"
Header Configuration:
- Any valid HTTP header name can be used as a key
- Header values must be strings
- Headers are added to all responses from the site
- Case-insensitive header names (will be normalized)
Site SSL/TLS Configuration
Configure SSL/TLS for HTTPS sites with automatic or manual certificates.
[[sites]]
name = "secure"
hostname = "secure.example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true # Enable SSL/TLS (boolean)
auto_cert = true # Use automatic certificates (boolean)
domains = ["secure.example.com", "www.secure.example.com"] # Additional domains (array)
cert_file = "/etc/ssl/certs/site.crt" # Certificate file path (string)
key_file = "/etc/ssl/private/site.key" # Private key file path (string)
[sites.ssl.acme]
enabled = true # Enable ACME certificate generation (boolean)
email = "admin@example.com" # Email for ACME registration (string)
staging = false # Use staging environment (boolean)
challenge_dir = "./acme-challenges" # ACME challenge directory (string)
SSL Parameters:
enabled(boolean, optional): Enable SSL/TLS for this site. Default:falseauto_cert(boolean, optional): Use automatic certificate generation via ACME. Default:falsedomains(array, optional): Additional domains for the SSL certificate. Default:[]cert_file(string, required if auto_cert=false): Path to SSL certificate filekey_file(string, required if auto_cert=false): Path to SSL private key file
ACME Configuration:
enabled(boolean, optional): Enable ACME certificate generation. Default:falseemail(string, required if enabled): Email address for ACME registrationstaging(boolean, optional): Use Let's Encrypt staging environment for testing. Default:falsechallenge_dir(string, optional): Directory for HTTP-01 challenge files. Default:"./acme-challenges"
SSL Configuration Examples:
Automatic SSL with ACME (Let's Encrypt):
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "./acme-challenges"
Manual SSL with custom certificates:
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "/etc/ssl/certs/example.com.crt"
key_file = "/etc/ssl/private/example.com.key"
Site Rate Limiting
Configure rate limiting for requests.
[[sites]]
name = "rate-limited"
hostname = "api.example.com"
port = 8080
static_dir = "static"
[sites.rate_limit]
enabled = true # Enable rate limiting (boolean)
requests_per_minute = 60 # Requests per minute per IP (integer)
burst_size = 10 # Burst allowance (integer)
block_duration = 300 # Block duration in seconds (integer)
whitelist = ["127.0.0.1", "10.0.0.0/8"] # IP whitelist (array)
blacklist = ["192.168.1.100"] # IP blacklist (array)
Rate Limiting Parameters:
enabled(boolean, optional): Enable rate limiting. Default:falserequests_per_minute(integer, optional): Maximum requests per minute per IP. Default:60burst_size(integer, optional): Allow burst of requests above the rate. Default:10block_duration(integer, optional): How long to block an IP after rate limit exceeded. Default:300whitelist(array, optional): IP addresses or CIDR blocks to exempt from rate limitingblacklist(array, optional): IP addresses or CIDR blocks to always block
Site Access Control
Configure access control and authentication.
[[sites]]
name = "protected"
hostname = "internal.example.com"
port = 8080
static_dir = "static"
[sites.access]
allow_ips = ["10.0.0.0/8", "192.168.0.0/16"] # Allowed IP ranges (array)
deny_ips = ["192.168.1.100"] # Denied IP addresses (array)
require_auth = true # Require authentication (boolean)
auth_type = "basic" # Authentication type (string)
auth_realm = "Protected Area" # Basic auth realm (string)
auth_file = "/etc/bws/htpasswd" # Password file for basic auth (string)
Access Control Parameters:
allow_ips(array, optional): IP addresses or CIDR blocks allowed accessdeny_ips(array, optional): IP addresses or CIDR blocks denied accessrequire_auth(boolean, optional): Require authentication for access. Default:falseauth_type(string, optional): Authentication method. Values:basic,digest. Default:basicauth_realm(string, optional): Realm name for HTTP authentication. Default:BWSauth_file(string, required if require_auth=true): Path to password file
Complete Configuration Example
# BWS Complete Configuration Example
# Daemon configuration
[daemon]
user = "bws"
group = "bws"
pid_file = "/var/run/bws.pid"
working_directory = "/opt/bws"
# Logging configuration
[logging]
level = "info"
output = "file"
format = "json"
file_path = "/var/log/bws/bws.log"
max_size = "100MB"
max_files = 10
compress = true
# Performance tuning
[performance]
worker_threads = 8
max_connections = 10000
keep_alive_timeout = 60
request_timeout = 30
read_buffer_size = "64KB"
write_buffer_size = "64KB"
# Monitoring
[monitoring]
enabled = true
health_endpoint = "/health"
detailed_endpoint = "/health/detailed"
[monitoring.checks]
disk_threshold = 90
memory_threshold = 80
response_time_threshold = 1000
# Caching
[caching]
enabled = true
max_memory = "1GB"
ttl_default = 3600
ttl_static = 86400
# Compression
[compression]
enabled = true
level = 6
min_size = 1024
types = ["text/html", "text/css", "application/javascript"]
# Main website (HTTP)
[[sites]]
name = "main"
hostname = "example.com"
port = 80
static_dir = "/var/www/main"
index_file = "index.html"
[sites.ssl]
enabled = false
[sites.headers]
"Cache-Control" = "public, max-age=3600"
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "SAMEORIGIN"
# Main website (HTTPS with auto SSL)
[[sites]]
name = "main_https"
hostname = "example.com"
port = 443
static_dir = "/var/www/main"
index_file = "index.html"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "/var/www/acme-challenges"
[sites.headers]
"Cache-Control" = "public, max-age=3600"
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "SAMEORIGIN"
"Strict-Transport-Security" = "max-age=31536000"
# API server (HTTPS with manual SSL)
[[sites]]
name = "api"
hostname = "api.example.com"
port = 443
static_dir = "/var/www/api-docs"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "/etc/ssl/certs/api.example.com.crt"
key_file = "/etc/ssl/private/api.example.com.key"
[sites.headers]
"Content-Type" = "application/json"
"Access-Control-Allow-Origin" = "https://example.com"
"Cache-Control" = "no-cache"
"Strict-Transport-Security" = "max-age=31536000"
[sites.rate_limit]
enabled = true
requests_per_minute = 100
burst_size = 20
# Secure admin interface
[[sites]]
name = "admin"
hostname = "admin.example.com"
port = 8443
static_dir = "/var/www/admin"
[sites.ssl]
enabled = true
cert_file = "/etc/ssl/certs/admin.crt"
key_file = "/etc/ssl/private/admin.key"
[sites.access]
allow_ips = ["10.0.0.0/8"]
require_auth = true
auth_file = "/etc/bws/admin.htpasswd"
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000"
"X-Frame-Options" = "DENY"
Data Types and Formats
String Values
- Quoted strings:
"value" - Raw strings:
'value'(no escape sequences) - Multi-line strings:
"""value"""
Size Values
Size values can use suffixes:
B- bytesKB- kilobytes (1024 bytes)MB- megabytes (1024 KB)GB- gigabytes (1024 MB)
Examples: "1MB", "512KB", "2GB"
Duration Values
Duration values are integers representing seconds unless otherwise specified.
Arrays
Arrays use square brackets: ["item1", "item2"]
IP Addresses and CIDR
- IPv4:
"192.168.1.1" - IPv6:
"2001:db8::1" - CIDR notation:
"192.168.0.0/16","10.0.0.0/8"
Configuration Validation
BWS validates configuration on startup. Common validation errors:
Syntax Errors
# Invalid: missing quotes
name = value # Should be name = "value"
# Invalid: missing comma in array
ports = [8080 8081] # Should be ports = [8080, 8081]
Type Errors
# Invalid: string instead of integer
port = "8080" # Should be port = 8080
# Invalid: integer instead of boolean
enabled = 1 # Should be enabled = true
Logical Errors
# Invalid: duplicate site names
[[sites]]
name = "main"
[[sites]]
name = "main" # Error: duplicate name
# Invalid: missing required fields
[[sites]]
hostname = "localhost" # Error: missing port and static_dir
Environment Variable Overrides
Some configuration values can be overridden with environment variables:
BWS_CONFIG=/path/to/config.toml # Configuration file path
BWS_LOG_FILE=/path/to/log/file # Override logging.file_path
BWS_PID_FILE=/path/to/pid/file # Override daemon.pid_file
BWS_STATIC_DIR=/path/to/static # Override sites.static_dir (first site)
BWS_PORT=8080 # Override sites.port (first site)
BWS_HOSTNAME=localhost # Override sites.hostname (first site)
RUST_LOG=debug # Override logging.level
Configuration Best Practices
Security
- Set appropriate file permissions (600) for configuration files
- Use separate configuration files for different environments
- Store sensitive data in environment variables or external secret management
- Regularly review and audit configuration changes
Performance
- Tune worker threads based on workload characteristics
- Configure appropriate timeouts for your use case
- Enable compression for text-based content
- Use caching for frequently accessed static files
Monitoring
- Enable health endpoints for monitoring
- Configure appropriate thresholds for alerts
- Use structured logging for better analysis
- Monitor resource usage trends
Maintenance
- Use version control for configuration files
- Document configuration changes
- Test configuration changes in non-production environments
- Implement configuration validation in CI/CD pipelines
Example Configurations
BWS includes several example configurations for different use cases:
Test Configurations
# Virtual hosting test (multiple sites on same port)
tests/test_multisite_shared_port.toml
# Multi-site test (different ports)
tests/config_test.toml
# Load balancing test
tests/test_load_balancing.toml
# WebSocket proxy test
tests/test_websocket_proxy.toml
Testing Virtual Hosting
The virtual hosting test demonstrates multiple sites sharing port 8080:
# Run the virtual hosting test
./tests/test_multisite_shared_port.sh test
# View the configuration
cat tests/test_multisite_shared_port.toml
This test includes:
- 4 virtual hosts: www.local.com, blog.local.com, api.local.com, dev.local.com
- Shared port: All sites use port 8080
- Host-based routing: Routes based on HTTP Host header
- Site-specific content: Each site serves different static files
- Custom headers: Site-specific response headers
Development Configurations
# Basic single site
examples/basic-single-site.toml
# Multi-site setup
examples/basic-multi-site.toml
# SSL with ACME
examples/ssl-acme.toml
# Production setup
examples/production-multi-site.toml
Configuration Templates
Create your own configuration based on these templates:
# Copy a template
cp tests/test_multisite_shared_port.toml my-config.toml
# Customize for your needs
# - Update hostnames to your domains
# - Change static_dir paths
# - Configure SSL settings
# - Add your custom headers
# Test the configuration
./target/release/bws --config my-config.toml
Next Steps
- Review Production Setup for deployment configurations
- Check Performance Tuning for optimization settings
- See Troubleshooting for configuration issues
Contributing to BWS
Thank you for your interest in contributing to BWS! This guide will help you get started with contributing to the project.
Getting Started
Prerequisites
Before contributing, ensure you have:
- Rust: Version 1.89 or later
- Git: For version control
- GitHub Account: For submitting pull requests
- Basic Rust Knowledge: Understanding of Rust syntax and concepts
Development Environment Setup
-
Fork the Repository
# Fork the repository on GitHub, then clone your fork git clone https://github.com/yourusername/bws.git cd bws # Add upstream remote git remote add upstream https://github.com/benliao/bws.git -
Install Dependencies
# Install Rust if not already installed curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.cargo/env # Install required tools cargo install cargo-fmt cargo install cargo-clippy cargo install cargo-audit -
Build the Project
# Build in debug mode cargo build # Run tests cargo test # Check formatting and linting cargo fmt --check cargo clippy -- -D warnings
Development Workflow
1. Creating a Feature Branch
# Sync with upstream
git fetch upstream
git checkout main
git merge upstream/main
# Create feature branch
git checkout -b feature/your-feature-name
2. Making Changes
Follow these guidelines when making changes:
Code Style
- Follow Rust standard formatting (
cargo fmt) - Use meaningful variable and function names
- Add documentation for public APIs
- Include unit tests for new functionality
Commit Messages
Use conventional commit format:
type(scope): description
body (optional)
footer (optional)
Examples:
git commit -m "feat(server): add HTTP/2 support"
git commit -m "fix(config): handle missing static directory"
git commit -m "docs(readme): update installation instructions"
Commit Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
3. Testing Your Changes
# Run all tests
cargo test
# Run specific test
cargo test test_name
# Run tests with output
cargo test -- --nocapture
# Run integration tests
cargo test --test integration
# Check for memory leaks (if available)
cargo valgrind test
4. Code Quality Checks
# Format code
cargo fmt
# Check formatting
cargo fmt --check
# Run linter
cargo clippy
# Check for security vulnerabilities
cargo audit
# Check documentation
cargo doc --no-deps
Contributing Guidelines
Code Organization
src/
โโโ lib.rs # Main library entry point
โโโ bin/
โ โโโ main.rs # Binary entry point
โโโ config/ # Configuration handling
โโโ server/ # Server implementation
โโโ handlers/ # Request handlers
โโโ utils/ # Utility functions
โโโ tests/ # Integration tests
tests/ # Integration tests
docs/ # Documentation
examples/ # Example configurations
Writing Good Code
1. Error Handling
#![allow(unused)] fn main() { use anyhow::{Context, Result}; fn read_config_file(path: &str) -> Result<Config> { let content = std::fs::read_to_string(path) .with_context(|| format!("Failed to read config file: {}", path))?; toml::from_str(&content) .with_context(|| "Failed to parse config file") } }
2. Logging
#![allow(unused)] fn main() { use tracing::{info, warn, error, debug}; fn start_server(config: &Config) -> Result<()> { info!("Starting BWS server on {}:{}", config.hostname, config.port); match bind_server(&config) { Ok(server) => { info!("Server started successfully"); server.run() } Err(e) => { error!("Failed to start server: {}", e); Err(e) } } } }
3. Documentation
#![allow(unused)] fn main() { /// Handles HTTP requests for static file serving /// /// # Arguments /// /// * `request` - The incoming HTTP request /// * `static_dir` - Path to the directory containing static files /// /// # Returns /// /// Returns a `Result` containing the HTTP response or an error /// /// # Examples /// /// ```rust /// let response = handle_static_request(&request, "/var/www/static")?; /// ``` pub fn handle_static_request( request: &HttpRequest, static_dir: &str ) -> Result<HttpResponse> { // Implementation here } }
4. Testing
#![allow(unused)] fn main() { #[cfg(test)] mod tests { use super::*; #[test] fn test_config_parsing() { let config_str = r#" [[sites]] name = "test" hostname = "localhost" port = 8080 static_dir = "static" "#; let config: Config = toml::from_str(config_str).unwrap(); assert_eq!(config.sites.len(), 1); assert_eq!(config.sites[0].name, "test"); } #[tokio::test] async fn test_server_startup() { let config = Config::default(); let result = start_server(&config).await; assert!(result.is_ok()); } } }
Pull Request Process
1. Before Submitting
- Code follows project style guidelines
-
All tests pass (
cargo test) -
Code is properly formatted (
cargo fmt) -
No clippy warnings (
cargo clippy) - Documentation is updated if needed
- CHANGELOG.md is updated for user-facing changes
2. Pull Request Template
## Description
Brief description of changes made.
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing performed
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Tests pass
- [ ] Documentation updated
- [ ] CHANGELOG.md updated
3. Review Process
- Automated Checks: CI/CD pipeline runs automatically
- Code Review: Maintainers review the changes
- Feedback: Address any requested changes
- Approval: Once approved, the PR will be merged
Issue Reporting
Bug Reports
Use the bug report template:
**Bug Description**
A clear description of the bug.
**Steps to Reproduce**
1. Step 1
2. Step 2
3. Step 3
**Expected Behavior**
What you expected to happen.
**Actual Behavior**
What actually happened.
**Environment**
- OS: [e.g., Ubuntu 20.04]
- Rust version: [e.g., 1.89.0]
- BWS version: [e.g., 0.1.5]
**Configuration**
```toml
# Include relevant configuration
Logs
Include relevant log output
#### Feature Requests
Use the feature request template:
```markdown
**Feature Description**
Clear description of the proposed feature.
**Use Case**
Explain why this feature would be useful.
**Proposed Solution**
Describe how you envision this feature working.
**Alternatives Considered**
Any alternative solutions you've considered.
**Additional Context**
Any other context about the feature request.
Development Best Practices
Code Review Guidelines
When reviewing code:
- Functionality: Does the code work correctly?
- Style: Does it follow project conventions?
- Performance: Are there performance implications?
- Security: Are there security concerns?
- Maintainability: Is the code easy to understand and maintain?
- Testing: Are there adequate tests?
Performance Considerations
- Use
cargo benchfor performance testing - Profile with
cargo flamegraphwhen needed - Consider memory allocation patterns
- Benchmark critical paths
- Document performance characteristics
Security Guidelines
- Validate all user inputs
- Use secure defaults
- Follow principle of least privilege
- Regular security audits with
cargo audit - Handle sensitive data carefully
- Document security assumptions
Development Tools
Recommended Tools
# Essential tools
cargo install cargo-watch # Auto-rebuild on changes
cargo install cargo-expand # Expand macros
cargo install cargo-tree # Dependency tree
cargo install cargo-outdated # Check for outdated dependencies
# Development helpers
cargo install cargo-edit # Add/remove dependencies easily
cargo install cargo-release # Release management
cargo install cargo-benchcmp # Compare benchmarks
IDE Setup
VS Code
Recommended extensions:
- rust-analyzer
- CodeLLDB (debugging)
- Better TOML
- GitLens
Settings
{
"rust-analyzer.checkOnSave.command": "clippy",
"rust-analyzer.cargo.features": "all",
"editor.formatOnSave": true
}
Debugging
# Debug build
cargo build
# Run with debugger
rust-gdb target/debug/bws
# Or with lldb
rust-lldb target/debug/bws
# Environment variables for debugging
RUST_BACKTRACE=1 cargo run
RUST_LOG=debug cargo run
Release Process
Version Numbering
BWS follows Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes (backward compatible)
Release Checklist
-
Update Version Numbers
# Update Cargo.toml version = "0.2.0" # Update documentation references -
Update CHANGELOG.md
## [0.2.0] - 2024-01-15 ### Added - New feature descriptions ### Changed - Changed feature descriptions ### Fixed - Bug fix descriptions -
Run Full Test Suite
cargo test --all-features cargo clippy --all-targets --all-features cargo fmt --check cargo audit -
Update Documentation
cargo doc --no-deps mdbook build docs/ -
Create Release Tag
git tag -a v0.2.0 -m "Release version 0.2.0" git push origin v0.2.0
Community Guidelines
Code of Conduct
We follow the Rust Code of Conduct:
- Be friendly and welcoming
- Be patient
- Be respectful
- Be constructive
- Choose your words carefully
Communication Channels
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: General questions and discussions
- Pull Requests: Code contributions and reviews
Getting Help
If you need help:
- Check existing documentation
- Search GitHub issues
- Ask in GitHub Discussions
- Ping maintainers in issues/PRs if urgent
Recognition
Contributors are recognized in:
- CONTRIBUTORS.md file
- Release notes
- GitHub contributor graphs
- Special thanks in documentation
Advanced Topics
Adding New Features
1. Design Document
For significant features, create a design document:
# Feature: HTTP/2 Support
## Overview
Add HTTP/2 support to BWS for improved performance.
## Motivation
- Better multiplexing
- Reduced latency
- Industry standard
## Design
- Use hyper's HTTP/2 implementation
- Maintain backward compatibility
- Configuration options for HTTP/2 settings
## Implementation Plan
1. Update dependencies
2. Add configuration options
3. Implement HTTP/2 handling
4. Add tests
5. Update documentation
## Testing Strategy
- Unit tests for new code
- Integration tests with HTTP/2 clients
- Performance benchmarks
- Compatibility testing
## Documentation Updates
- Configuration reference
- Performance guide
- Migration guide
2. Implementation Steps
- Create feature branch
- Add configuration options
- Implement core functionality
- Add comprehensive tests
- Update documentation
- Submit pull request
Performance Optimization
When optimizing performance:
- Measure First: Use benchmarks to identify bottlenecks
- Profile: Use profiling tools to understand behavior
- Optimize: Make targeted improvements
- Verify: Confirm improvements with benchmarks
- Document: Update performance documentation
Dependency Management
# Add dependency
cargo add tokio --features full
# Add dev dependency
cargo add --dev criterion
# Update dependencies
cargo update
# Check for outdated dependencies
cargo outdated
# Audit for security issues
cargo audit
Troubleshooting Development Issues
Common Issues
1. Build Failures
# Clean and rebuild
cargo clean
cargo build
# Check for dependency issues
cargo tree
cargo update
2. Test Failures
# Run specific test
cargo test test_name -- --nocapture
# Run ignored tests
cargo test -- --ignored
# Run tests in single thread
cargo test -- --test-threads=1
3. Formatting Issues
# Format all code
cargo fmt
# Check specific file
rustfmt src/lib.rs
4. Linting Issues
# Run clippy with all targets
cargo clippy --all-targets
# Allow specific lints
#[allow(clippy::too_many_arguments)]
Resources
Learning Resources
Tools and Libraries
- tokio - Async runtime
- hyper - HTTP library
- pingora - HTTP proxy framework
- tracing - Logging and diagnostics
Similar Projects
- nginx - High-performance web server
- caddy - Modern web server
- traefik - Cloud-native application proxy
Thank you for contributing to BWS! Your contributions help make the project better for everyone.
Building from Source
This guide covers building BWS from source code, including development setup, build options, and cross-compilation.
Prerequisites
System Requirements
Minimum Requirements
- RAM: 2GB available memory
- Disk Space: 1GB free space for build artifacts
- CPU: Any modern x64 or ARM64 processor
Supported Platforms
- Linux: Ubuntu 18.04+, Debian 10+, RHEL 8+, Alpine Linux
- macOS: 10.15+ (Catalina)
- Windows: Windows 10+ (with WSL2 recommended)
- FreeBSD: 12.0+
Dependencies
Rust Toolchain
# Install Rust via rustup (recommended)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Verify installation
rustc --version
cargo --version
# Update to latest stable
rustup update stable
System Dependencies
Ubuntu/Debian:
sudo apt update
sudo apt install -y \
build-essential \
pkg-config \
libssl-dev \
cmake \
git \
curl
CentOS/RHEL/Fedora:
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y \
pkg-config \
openssl-devel \
cmake \
git \
curl
macOS:
# Install Xcode command line tools
xcode-select --install
# Or install via Homebrew
brew install cmake pkg-config openssl
Windows (WSL2):
# In WSL2 Ubuntu
sudo apt update
sudo apt install -y \
build-essential \
pkg-config \
libssl-dev \
cmake \
git \
curl
Optional Dependencies
# For enhanced compression support
sudo apt install -y libbrotli-dev zlib1g-dev
# For performance profiling
cargo install cargo-flamegraph
# For security auditing
cargo install cargo-audit
# For benchmarking
cargo install cargo-bench
Getting the Source Code
Clone Repository
# Clone the main repository
git clone https://github.com/benliao/bws.git
cd bws
# Or clone your fork
git clone https://github.com/yourusername/bws.git
cd bws
# Check available branches/tags
git branch -a
git tag -l
Download Source Archive
# Download specific version
wget https://github.com/benliao/bws/archive/refs/tags/v0.1.5.tar.gz
tar -xzf v0.1.5.tar.gz
cd bws-0.1.5
Build Configuration
Cargo.toml Overview
[package]
name = "bws"
version = "0.1.5"
edition = "2021"
rust-version = "1.89"
[dependencies]
pingora = "0.6.0"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
toml = "0.8"
anyhow = "1.0"
tracing = "0.1"
[features]
default = ["compression", "metrics"]
compression = ["brotli", "gzip"]
metrics = ["prometheus"]
tls = ["openssl"]
jemalloc = ["jemallocator"]
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
Feature Flags
Available Features
compression: Enable response compression (gzip, brotli)metrics: Enable Prometheus metrics exporttls: Enable TLS/SSL supportjemalloc: Use jemalloc allocator for better performancemimalloc: Use mimalloc allocator (alternative to jemalloc)
Building with Specific Features
# Build with all features
cargo build --all-features
# Build with specific features
cargo build --features "compression,metrics"
# Build without default features
cargo build --no-default-features
# Build with custom feature combination
cargo build --no-default-features --features "compression,tls"
Build Commands
Development Build
# Standard debug build
cargo build
# With specific features
cargo build --features "compression,metrics"
# With verbose output
cargo build --verbose
# Check without building
cargo check
Release Build
# Optimized release build
cargo build --release
# Release with all features
cargo build --release --all-features
# Strip debug symbols
cargo build --release
strip target/release/bws # Linux/macOS
Custom Profiles
Performance Profile
# Add to Cargo.toml
[profile.performance]
inherits = "release"
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
# Build with performance profile
cargo build --profile performance
Size-Optimized Profile
[profile.min-size]
inherits = "release"
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
# Build for minimum size
cargo build --profile min-size
Build Optimization
Rust Compiler Flags
Environment Variables
# Enable link-time optimization
export RUSTFLAGS="-C link-arg=-s"
# Use specific target CPU
export RUSTFLAGS="-C target-cpu=native"
# Optimize for size
export RUSTFLAGS="-C opt-level=z"
# Build with optimizations
cargo build --release
Target-Specific Optimization
# Build for specific CPU architecture
RUSTFLAGS="-C target-cpu=skylake" cargo build --release
# Build with all CPU features
RUSTFLAGS="-C target-cpu=native" cargo build --release
# Build for compatibility
RUSTFLAGS="-C target-cpu=x86-64" cargo build --release
Memory Allocator Optimization
Using jemalloc
# Add to Cargo.toml
[dependencies]
jemallocator = "0.5"
# Add to src/main.rs
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
Using mimalloc
# Add to Cargo.toml
[dependencies]
mimalloc = { version = "0.1", default-features = false }
# Add to src/main.rs
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
Link-Time Optimization
# Cargo.toml release profile
[profile.release]
lto = true # Enable LTO
codegen-units = 1 # Use single codegen unit
opt-level = 3 # Maximum optimization
Cross-Compilation
Setup Cross-Compilation Targets
# Add common targets
rustup target add x86_64-unknown-linux-gnu
rustup target add x86_64-unknown-linux-musl
rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
rustup target add x86_64-pc-windows-gnu
# List installed targets
rustup target list --installed
Cross-Compilation Examples
Linux to musl (static linking)
# Install musl target
rustup target add x86_64-unknown-linux-musl
# Install musl tools (Ubuntu/Debian)
sudo apt install musl-tools
# Build static binary
cargo build --release --target x86_64-unknown-linux-musl
# Verify static linking
ldd target/x86_64-unknown-linux-musl/release/bws
# Should show "not a dynamic executable"
Linux to ARM64
# Install ARM64 target
rustup target add aarch64-unknown-linux-gnu
# Install cross-compilation tools
sudo apt install gcc-aarch64-linux-gnu
# Set up linker
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
# Build for ARM64
cargo build --release --target aarch64-unknown-linux-gnu
macOS Universal Binary
# Install both targets
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
# Build for both architectures
cargo build --release --target x86_64-apple-darwin
cargo build --release --target aarch64-apple-darwin
# Create universal binary
lipo -create \
target/x86_64-apple-darwin/release/bws \
target/aarch64-apple-darwin/release/bws \
-output target/release/bws-universal
Windows Cross-Compilation (from Linux)
# Install Windows target
rustup target add x86_64-pc-windows-gnu
# Install MinGW
sudo apt install mingw-w64
# Build for Windows
cargo build --release --target x86_64-pc-windows-gnu
Using cross Tool
# Install cross
cargo install cross
# Build for different targets using Docker
cross build --release --target aarch64-unknown-linux-gnu
cross build --release --target x86_64-unknown-linux-musl
cross build --release --target armv7-unknown-linux-gnueabihf
Build Scripts and Automation
Makefile
# Makefile
.PHONY: build build-release test clean install
# Default target
all: build
# Development build
build:
cargo build
# Release build
build-release:
cargo build --release
# Build with all features
build-all:
cargo build --release --all-features
# Run tests
test:
cargo test
# Clean build artifacts
clean:
cargo clean
# Install locally
install:
cargo install --path .
# Cross-compilation targets
build-linux-musl:
cargo build --release --target x86_64-unknown-linux-musl
build-arm64:
cargo build --release --target aarch64-unknown-linux-gnu
build-windows:
cargo build --release --target x86_64-pc-windows-gnu
# All targets
build-all-targets: build-linux-musl build-arm64 build-windows
# Package release
package:
mkdir -p dist
cp target/release/bws dist/
cp README.md LICENSE dist/
tar -czf dist/bws-$(shell cargo pkgid | cut -d'#' -f2).tar.gz -C dist .
Build Script
#!/bin/bash
# build.sh
set -e
echo "Building BWS from source..."
# Check Rust installation
if ! command -v cargo &> /dev/null; then
echo "Error: Rust/Cargo not found. Please install Rust first."
exit 1
fi
# Get version
VERSION=$(cargo pkgid | cut -d'#' -f2)
echo "Building BWS version $VERSION"
# Clean previous builds
echo "Cleaning previous builds..."
cargo clean
# Build release version
echo "Building release version..."
cargo build --release --all-features
# Run tests
echo "Running tests..."
cargo test --release
# Check binary
if [ -f "target/release/bws" ]; then
echo "Build successful!"
echo "Binary location: target/release/bws"
echo "Binary size: $(du -h target/release/bws | cut -f1)"
# Test the binary
./target/release/bws --version
else
echo "Build failed: Binary not found"
exit 1
fi
echo "Build completed successfully!"
GitHub Actions Build
# .github/workflows/build.yml
name: Build
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
rust: [stable, beta]
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Build
run: cargo build --release --all-features
- name: Run tests
run: cargo test --release
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: bws-${{ matrix.os }}
path: target/release/bws*
Testing the Build
Unit Tests
# Run all tests
cargo test
# Run specific test
cargo test test_config
# Run tests with output
cargo test -- --nocapture
# Run tests in single thread
cargo test -- --test-threads=1
# Run ignored tests
cargo test -- --ignored
Integration Tests
# Run integration tests only
cargo test --test integration
# Run specific integration test
cargo test --test integration -- test_server_startup
Benchmarks
# Install criterion for benchmarking
cargo install criterion
# Run benchmarks
cargo bench
# Run specific benchmark
cargo bench bench_name
Manual Testing
# Build and test the binary
cargo build --release
# Test basic functionality
./target/release/bws --version
./target/release/bws --help
# Test with sample configuration
echo '
[[sites]]
name = "test"
hostname = "127.0.0.1"
port = 8080
static_dir = "static"
' > test-config.toml
mkdir -p static
echo "Hello, World!" > static/index.html
# Start server in background
./target/release/bws --config test-config.toml &
SERVER_PID=$!
# Test HTTP request
sleep 2
curl http://127.0.0.1:8080/
# Clean up
kill $SERVER_PID
rm -rf test-config.toml static/
Troubleshooting Build Issues
Common Build Errors
1. Linker Errors
# Error: linker `cc` not found
sudo apt install build-essential # Ubuntu/Debian
sudo dnf groupinstall "Development Tools" # RHEL/Fedora
# Error: could not find system library 'openssl'
sudo apt install libssl-dev pkg-config # Ubuntu/Debian
sudo dnf install openssl-devel pkgconf # RHEL/Fedora
2. Memory Issues
# Reduce parallel builds if running out of memory
cargo build --jobs 1
# Or set permanently
echo 'jobs = 1' >> ~/.cargo/config.toml
3. Network Issues
# Use git instead of HTTPS for dependencies
git config --global url."git://github.com/".insteadOf "https://github.com/"
# Or use offline mode with vendored dependencies
cargo vendor
cargo build --offline
4. Permission Issues
# Fix cargo directory permissions
sudo chown -R $(whoami) ~/.cargo
# Clean and rebuild
cargo clean
cargo build
Debugging Build Issues
Verbose Output
# Build with verbose output
cargo build --verbose
# Show build timing
cargo build --timings
# Show why dependencies are being rebuilt
cargo build --verbose --explain
Environment Debugging
# Show cargo configuration
cargo config list
# Show target information
rustc --print target-list
rustc --print cfg
# Show toolchain information
rustup show
Installation
Local Installation
# Install from current directory
cargo install --path .
# Install with specific features
cargo install --path . --features "compression,metrics"
# Force reinstall
cargo install --path . --force
System-Wide Installation
# Copy binary to system path
sudo cp target/release/bws /usr/local/bin/
# Make executable
sudo chmod +x /usr/local/bin/bws
# Verify installation
bws --version
Package Creation
DEB Package (Ubuntu/Debian)
# Install cargo-deb
cargo install cargo-deb
# Add metadata to Cargo.toml
[package.metadata.deb]
maintainer = "Your Name <your.email@example.com>"
copyright = "2024, Your Name <your.email@example.com>"
license-file = ["LICENSE", "5"]
extended-description = "BWS is a high-performance multi-site web server"
depends = "$auto"
section = "utility"
priority = "optional"
assets = [
["target/release/bws", "usr/bin/", "755"],
["README.md", "usr/share/doc/bws/README", "644"],
]
# Build DEB package
cargo deb
RPM Package (RHEL/Fedora)
# Install cargo-rpm
cargo install cargo-rpm
# Initialize RPM spec
cargo rpm init
# Build RPM package
cargo rpm build
Windows Installer
# Install cargo-wix
cargo install cargo-wix
# Initialize WiX configuration
cargo wix init
# Build MSI installer
cargo wix
Optimization Tips
Build Performance
- Use
cargo checkduring development instead ofcargo build - Enable incremental compilation
- Use
sccachefor shared build cache - Consider using
moldlinker on Linux for faster linking
Binary Size Optimization
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
strip = true # Strip symbols
panic = "abort" # Smaller panic handling
Runtime Performance
[profile.release]
opt-level = 3 # Maximum optimization
lto = "fat" # Full LTO
codegen-units = 1 # Single codegen unit
panic = "abort" # Abort on panic
Next Steps
After building BWS:
- Read the Configuration Guide to set up your server
- Follow the Quick Start to get running
- Check Performance Tuning for optimization
- Review Production Setup for deployment
For development:
- Read the Contributing Guide
- Set up your development environment
- Run the test suite
- Start contributing!
Testing
This guide covers testing strategies, tools, and best practices for BWS development and deployment.
Testing Overview
BWS includes multiple layers of testing:
- Unit Tests: Test individual functions and modules using Rust's built-in test framework
- Integration Tests: Test component interactions and API endpoints
- Configuration Tests: Validate configuration files using
--dry-run - End-to-End Tests: Test complete workflows including hot reload and multi-site functionality
- Performance Tests: Measure performance characteristics under load
- Security Tests: Validate security measures and headers
Configuration Validation Testing
BWS includes comprehensive configuration validation that can be used for testing:
Using --dry-run for Testing
# Validate configuration files without starting server
bws --config config.toml --dry-run
# Test all example configurations
for config in examples/*.toml; do
echo "Testing $config..."
bws --config "$config" --dry-run
done
# Test production configurations
bws --config production.toml --dry-run
Automated Configuration Testing
BWS includes automated test scripts for comprehensive configuration validation:
# Run comprehensive configuration validation
./tests/scripts/validate-configs.sh
# Test specific configuration categories
./tests/scripts/validate-configs.sh --examples-only
./tests/scripts/validate-configs.sh --tests-only
Running Tests
Basic Test Commands
# Run all Rust unit tests
cargo test
# Run tests with output
cargo test -- --nocapture
# Run specific test
cargo test test_config_parsing
# Run tests matching pattern
cargo test config
# Run ignored tests
cargo test -- --ignored
# Run tests in single thread (for debugging)
cargo test -- --test-threads=1
Integration Test Scripts
BWS includes organized test scripts for comprehensive testing:
# Configuration validation tests
./tests/scripts/validate-configs.sh
# HTTP header functionality tests
./tests/scripts/test_headers.sh
# Multi-site hosting tests
./tests/scripts/test_multisite.sh
# Load balancing tests
./tests/scripts/test_load_balance.sh
# WebSocket proxy tests
./tests/scripts/simple_websocket_test.sh
# Hot reload functionality tests
./tests/scripts/test_hot_reload.sh
# Static file serving tests
./tests/scripts/test_static_server.sh
Test Categories
# Run only unit tests
cargo test --lib
# Run only integration tests
cargo test --test integration
# Run only documentation tests
cargo test --doc
# Run tests for specific package
cargo test -p bws-core
Test with Features
# Test with all features
cargo test --all-features
# Test with specific features
cargo test --features "compression,metrics"
# Test without default features
cargo test --no-default-features
Unit Testing
Basic Unit Tests
#![allow(unused)] fn main() { // src/config.rs #[cfg(test)] mod tests { use super::*; #[test] fn test_config_default() { let config = Config::default(); assert_eq!(config.sites.len(), 0); } #[test] fn test_config_parsing() { let toml_str = r#" [[sites]] name = "test" hostname = "localhost" port = 8080 static_dir = "static" "#; let config: Config = toml::from_str(toml_str).unwrap(); assert_eq!(config.sites.len(), 1); assert_eq!(config.sites[0].name, "test"); assert_eq!(config.sites[0].port, 8080); } #[test] #[should_panic(expected = "Invalid port")] fn test_invalid_port() { let site = Site { name: "test".to_string(), hostname: "localhost".to_string(), port: 0, // Invalid port static_dir: "static".to_string(), ..Default::default() }; site.validate().unwrap(); } } }
Testing Error Conditions
#![allow(unused)] fn main() { #[cfg(test)] mod tests { use super::*; use anyhow::Result; #[test] fn test_file_not_found() { let result = read_config_file("nonexistent.toml"); assert!(result.is_err()); let error = result.unwrap_err(); assert!(error.to_string().contains("No such file")); } #[test] fn test_invalid_toml() { let invalid_toml = "invalid toml content [[["; let result = parse_config(invalid_toml); assert!(result.is_err()); } #[test] fn test_missing_required_field() { let toml_str = r#" [[sites]] name = "test" Missing hostname, port, static_dir "#; let result: Result<Config, _> = toml::from_str(toml_str); assert!(result.is_err()); } } }
Mocking and Test Doubles
#![allow(unused)] fn main() { // Use mockall for mocking use mockall::predicate::*; use mockall::mock; mock! { FileSystem { fn read_file(&self, path: &str) -> Result<String>; fn file_exists(&self, path: &str) -> bool; } } #[cfg(test)] mod tests { use super::*; #[test] fn test_config_loading_with_mock() { let mut mock_fs = MockFileSystem::new(); mock_fs .expect_read_file() .with(eq("config.toml")) .times(1) .returning(|_| Ok(r#" [[sites]] name = "test" hostname = "localhost" port = 8080 static_dir = "static" "#.to_string())); let config = load_config_with_fs(&mock_fs, "config.toml").unwrap(); assert_eq!(config.sites.len(), 1); } } }
Integration Testing
Test Structure
#![allow(unused)] fn main() { // tests/integration/server_tests.rs use bws::{Config, Server}; use std::time::Duration; use tokio::time::sleep; #[tokio::test] async fn test_server_startup_shutdown() { let config = test_config(); let server = Server::new(config).await.unwrap(); // Start server in background let handle = tokio::spawn(async move { server.run().await }); // Give server time to start sleep(Duration::from_millis(100)).await; // Test server is responding let response = reqwest::get("http://127.0.0.1:8080/health").await.unwrap(); assert_eq!(response.status(), 200); // Shutdown server handle.abort(); } #[tokio::test] async fn test_static_file_serving() { let temp_dir = setup_test_static_files().await; let config = Config { sites: vec![Site { name: "test".to_string(), hostname: "127.0.0.1".to_string(), port: 8081, static_dir: temp_dir.path().to_string_lossy().to_string(), ..Default::default() }], ..Default::default() }; let server = Server::new(config).await.unwrap(); let handle = tokio::spawn(async move { server.run().await }); sleep(Duration::from_millis(100)).await; // Test serving static file let response = reqwest::get("http://127.0.0.1:8081/test.html").await.unwrap(); assert_eq!(response.status(), 200); assert_eq!(response.text().await.unwrap(), "<h1>Test</h1>"); handle.abort(); cleanup_test_files(temp_dir).await; } fn test_config() -> Config { Config { sites: vec![Site { name: "test".to_string(), hostname: "127.0.0.1".to_string(), port: 8080, static_dir: "test_static".to_string(), ..Default::default() }], ..Default::default() } } async fn setup_test_static_files() -> tempfile::TempDir { let temp_dir = tempfile::tempdir().unwrap(); tokio::fs::write( temp_dir.path().join("test.html"), "<h1>Test</h1>" ).await.unwrap(); tokio::fs::write( temp_dir.path().join("index.html"), "<h1>Index</h1>" ).await.unwrap(); temp_dir } }
HTTP Client Testing
#![allow(unused)] fn main() { // tests/integration/http_tests.rs use reqwest::Client; use serde_json::Value; #[tokio::test] async fn test_health_endpoint() { let client = Client::new(); let response = client .get("http://127.0.0.1:8080/health") .send() .await .unwrap(); assert_eq!(response.status(), 200); assert_eq!(response.headers().get("content-type").unwrap(), "application/json"); let body: Value = response.json().await.unwrap(); assert_eq!(body["status"], "healthy"); } #[tokio::test] async fn test_custom_headers() { let client = Client::new(); let response = client .get("http://127.0.0.1:8080/") .send() .await .unwrap(); // Check custom headers are present assert!(response.headers().contains_key("x-served-by")); assert_eq!(response.headers()["cache-control"], "public, max-age=3600"); } #[tokio::test] async fn test_cors_headers() { let client = Client::new(); let response = client .options("http://127.0.0.1:8080/") .header("Origin", "https://example.com") .header("Access-Control-Request-Method", "GET") .send() .await .unwrap(); assert_eq!(response.status(), 200); assert!(response.headers().contains_key("access-control-allow-origin")); } }
Database Integration Tests
#![allow(unused)] fn main() { // tests/integration/database_tests.rs (if BWS had database features) use sqlx::PgPool; #[tokio::test] async fn test_database_connection() { let pool = setup_test_database().await; let config = Config { database_url: Some(pool.connect_options().to_url_lossy().to_string()), ..test_config() }; let server = Server::new(config).await.unwrap(); // Test database-dependent endpoints let response = reqwest::get("http://127.0.0.1:8080/api/data").await.unwrap(); assert_eq!(response.status(), 200); cleanup_test_database(pool).await; } async fn setup_test_database() -> PgPool { // Set up test database PgPool::connect("postgres://test:test@localhost/bws_test") .await .unwrap() } }
End-to-End Testing
Test Scenarios
#![allow(unused)] fn main() { // tests/e2e/scenarios.rs use std::process::{Command, Stdio}; use std::time::Duration; use tokio::time::sleep; #[tokio::test] async fn test_complete_deployment_scenario() { // 1. Create test configuration let config_content = r#" [daemon] pid_file = "/tmp/bws-test.pid" [logging] level = "info" output = "file" file_path = "/tmp/bws-test.log" [[sites]] name = "main" hostname = "127.0.0.1" port = 8080 static_dir = "test_static" [sites.headers] "Cache-Control" = "public, max-age=3600" "#; std::fs::write("test-config.toml", config_content).unwrap(); // 2. Create static files std::fs::create_dir_all("test_static").unwrap(); std::fs::write("test_static/index.html", "<h1>Welcome to BWS</h1>").unwrap(); std::fs::write("test_static/style.css", "body { color: blue; }").unwrap(); // 3. Start BWS server let mut child = Command::new("target/release/bws") .arg("--config") .arg("test-config.toml") .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn() .unwrap(); // 4. Wait for server to start sleep(Duration::from_secs(2)).await; // 5. Run tests test_homepage().await; test_static_files().await; test_health_check().await; test_performance().await; // 6. Cleanup child.kill().unwrap(); std::fs::remove_file("test-config.toml").unwrap(); std::fs::remove_dir_all("test_static").unwrap(); std::fs::remove_file("/tmp/bws-test.log").ok(); std::fs::remove_file("/tmp/bws-test.pid").ok(); } async fn test_homepage() { let response = reqwest::get("http://127.0.0.1:8080/").await.unwrap(); assert_eq!(response.status(), 200); assert!(response.text().await.unwrap().contains("Welcome to BWS")); } async fn test_static_files() { let response = reqwest::get("http://127.0.0.1:8080/style.css").await.unwrap(); assert_eq!(response.status(), 200); assert_eq!(response.headers()["content-type"], "text/css"); assert!(response.text().await.unwrap().contains("color: blue")); } async fn test_health_check() { let response = reqwest::get("http://127.0.0.1:8080/health").await.unwrap(); assert_eq!(response.status(), 200); let health: serde_json::Value = response.json().await.unwrap(); assert_eq!(health["status"], "healthy"); } async fn test_performance() { use std::time::Instant; let start = Instant::now(); // Make 100 concurrent requests let futures: Vec<_> = (0..100) .map(|_| reqwest::get("http://127.0.0.1:8080/")) .collect(); let responses = futures::future::join_all(futures).await; let duration = start.elapsed(); // All requests should succeed for response in responses { assert_eq!(response.unwrap().status(), 200); } // Should complete in reasonable time assert!(duration < Duration::from_secs(5)); println!("100 requests completed in {:?}", duration); } }
Multi-Site Testing
#![allow(unused)] fn main() { #[tokio::test] async fn test_multi_site_configuration() { let config_content = r#" [[sites]] name = "main" hostname = "127.0.0.1" port = 8080 static_dir = "main_static" [[sites]] name = "api" hostname = "127.0.0.1" port = 8081 static_dir = "api_static" [sites.headers] "Content-Type" = "application/json" "#; // Setup and test both sites setup_multi_site_files(); let mut child = start_bws_server("multi-site-config.toml"); sleep(Duration::from_secs(2)).await; // Test main site let response = reqwest::get("http://127.0.0.1:8080/").await.unwrap(); assert_eq!(response.status(), 200); // Test API site let response = reqwest::get("http://127.0.0.1:8081/").await.unwrap(); assert_eq!(response.status(), 200); assert_eq!(response.headers()["content-type"], "application/json"); cleanup_multi_site_test(child); } }
Virtual Hosting (Shared Port) Testing
BWS supports virtual hosting where multiple sites share the same port but are distinguished by hostname. This is particularly useful for hosting multiple domains on standard ports (80/443).
Test Configuration
# tests/test_multisite_shared_port.toml
[server]
name = "BWS Multi-Site Shared Port Test Server"
# All sites share port 8080 but have different hostnames
[[sites]]
name = "main"
hostname = "www.local.com"
port = 8080
static_dir = "examples/sites/static"
default = true
[sites.headers]
"X-Site-Name" = "Main Site"
"X-Port-Sharing" = "enabled"
[[sites]]
name = "blog"
hostname = "blog.local.com"
port = 8080
static_dir = "examples/sites/static-blog"
[sites.headers]
"X-Site-Name" = "Blog Site"
"X-Port-Sharing" = "enabled"
[[sites]]
name = "api"
hostname = "api.local.com"
port = 8080
static_dir = "examples/sites/static-api"
[sites.headers]
"X-Site-Name" = "API Documentation"
"X-Port-Sharing" = "enabled"
Running Virtual Hosting Tests
# Automated test script
./tests/test_multisite_shared_port.sh test
# Manual testing with Host headers
curl -H "Host: www.local.com" http://127.0.0.1:8080
curl -H "Host: blog.local.com" http://127.0.0.1:8080
curl -H "Host: api.local.com" http://127.0.0.1:8080
# Check site-specific headers
curl -I -H "Host: www.local.com" http://127.0.0.1:8080
Setting Up Local Testing
For browser testing, add domains to /etc/hosts:
sudo bash -c 'echo "127.0.0.1 www.local.com blog.local.com api.local.com dev.local.com" >> /etc/hosts'
Then access:
- http://www.local.com:8080 (Main Site)
- http://blog.local.com:8080 (Blog)
- http://api.local.com:8080 (API Docs)
- http://dev.local.com:8080 (Development)
Virtual Hosting Integration Test
#![allow(unused)] fn main() { #[tokio::test] async fn test_virtual_hosting_shared_port() { setup_virtual_hosting_sites(); let mut child = start_bws_server("test_multisite_shared_port.toml"); sleep(Duration::from_secs(2)).await; // Test main site let client = reqwest::Client::new(); let response = client .get("http://127.0.0.1:8080/") .header("Host", "www.local.com") .send() .await .unwrap(); assert_eq!(response.status(), 200); assert_eq!(response.headers()["x-site-name"], "Main Site"); assert_eq!(response.headers()["x-port-sharing"], "enabled"); // Test blog site (same port, different hostname) let response = client .get("http://127.0.0.1:8080/") .header("Host", "blog.local.com") .send() .await .unwrap(); assert_eq!(response.status(), 200); assert_eq!(response.headers()["x-site-name"], "Blog Site"); assert_eq!(response.headers()["x-port-sharing"], "enabled"); // Test API site let response = client .get("http://127.0.0.1:8080/") .header("Host", "api.local.com") .send() .await .unwrap(); assert_eq!(response.status(), 200); assert_eq!(response.headers()["x-site-name"], "API Documentation"); assert_eq!(response.headers()["x-port-sharing"], "enabled"); cleanup_virtual_hosting_test(child); } }
WebSocket Proxy Testing
#![allow(unused)] fn main() { // tests/integration/websocket_tests.rs use tokio_tungstenite::{connect_async, tungstenite::Message}; #[tokio::test] async fn test_websocket_proxy_configuration() { let config_content = r#" [[sites]] name = "websocket-proxy" hostname = "127.0.0.1" port = 8090 static_dir = "static" [sites.proxy] enabled = true [[sites.proxy.upstreams]] name = "websocket_backend" url = "http://127.0.0.1:3001" weight = 1 [[sites.proxy.upstreams]] name = "websocket_backend" url = "http://127.0.0.1:3002" weight = 1 [[sites.proxy.routes]] path = "/ws" upstream = "websocket_backend" strip_prefix = true websocket = true [sites.proxy.load_balancing] method = "round_robin" "#; // Start mock WebSocket servers let server1 = start_mock_websocket_server(3001).await; let server2 = start_mock_websocket_server(3002).await; // Start BWS with WebSocket proxy config let bws_server = start_bws_server_with_config(config_content).await; // Test WebSocket upgrade detection test_websocket_upgrade_detection().await; // Test WebSocket proxy connection test_websocket_proxy_connection().await; // Test load balancing test_websocket_load_balancing().await; // Cleanup cleanup_websocket_test(bws_server, server1, server2).await; } async fn test_websocket_upgrade_detection() { // Test that BWS properly detects WebSocket upgrade requests let client = reqwest::Client::new(); let response = client .get("http://127.0.0.1:8090/ws") .header("Upgrade", "websocket") .header("Connection", "Upgrade") .header("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==") .header("Sec-WebSocket-Version", "13") .send() .await .unwrap(); // Should attempt WebSocket upgrade (not 404) assert_ne!(response.status(), 404); } async fn test_websocket_proxy_connection() { // Note: This test demonstrates the framework // Full implementation requires additional Pingora integration // Attempt WebSocket connection through proxy let ws_url = "ws://127.0.0.1:8090/ws"; // In a complete implementation, this would succeed match connect_async(ws_url).await { Ok((mut ws_stream, _)) => { // Send test message ws_stream.send(Message::Text("test".to_string())).await.unwrap(); // Receive response if let Some(msg) = ws_stream.next().await { let msg = msg.unwrap(); assert!(msg.is_text()); println!("Received: {}", msg.to_text().unwrap()); } } Err(e) => { // Expected in current implementation println!("WebSocket connection failed (expected): {}", e); } } } async fn test_websocket_load_balancing() { // Test that WebSocket connections are load balanced let mut server_responses = std::collections::HashMap::new(); // Make multiple connections for i in 0..10 { let ws_url = format!("ws://127.0.0.1:8090/ws?test={}", i); // In full implementation, track which server responds // Current implementation provides detection framework match connect_async(&ws_url).await { Ok((mut ws_stream, _)) => { ws_stream.send(Message::Text("ping".to_string())).await.unwrap(); if let Some(msg) = ws_stream.next().await { let response = msg.unwrap().to_text().unwrap(); *server_responses.entry(response.to_string()).or_insert(0) += 1; } } Err(_) => { // Expected in current framework implementation } } } // In full implementation, verify load balancing distribution println!("Server response distribution: {:?}", server_responses); } async fn start_mock_websocket_server(port: u16) -> tokio::task::JoinHandle<()> { tokio::spawn(async move { use tokio_tungstenite::{accept_async, tungstenite::Message}; use tokio::net::{TcpListener, TcpStream}; let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).await.unwrap(); println!("Mock WebSocket server listening on port {}", port); while let Ok((stream, _)) = listener.accept().await { tokio::spawn(handle_websocket_connection(stream, port)); } }) } async fn handle_websocket_connection(stream: TcpStream, port: u16) { match accept_async(stream).await { Ok(mut websocket) => { while let Some(msg) = websocket.next().await { match msg { Ok(Message::Text(text)) => { let response = format!("Echo from server {}: {}", port, text); websocket.send(Message::Text(response)).await.unwrap(); } Ok(Message::Close(_)) => break, Err(e) => { println!("WebSocket error: {}", e); break; } _ => {} } } } Err(e) => println!("WebSocket handshake failed: {}", e), } } }
WebSocket Test Script
#!/bin/bash
# Run the WebSocket proxy test script
./tests/test_websocket_proxy.sh
This script will:
- Start multiple WebSocket test servers
- Configure BWS with WebSocket proxy routes
- Provide a web interface for manual testing
- Demonstrate load balancing between upstream servers
Performance Testing
Load Testing with wrk
#!/bin/bash
# scripts/load-test.sh
BWS_PID=""
setup_test_server() {
echo "Setting up test server..."
# Create test configuration
cat > test-load-config.toml << EOF
[[sites]]
name = "load-test"
hostname = "127.0.0.1"
port = 8080
static_dir = "load_test_static"
[sites.headers]
"Cache-Control" = "public, max-age=3600"
EOF
# Create test files
mkdir -p load_test_static
echo "<h1>Load Test Page</h1>" > load_test_static/index.html
# Generate test files of various sizes
dd if=/dev/zero of=load_test_static/1kb.txt bs=1024 count=1 2>/dev/null
dd if=/dev/zero of=load_test_static/10kb.txt bs=1024 count=10 2>/dev/null
dd if=/dev/zero of=load_test_static/100kb.txt bs=1024 count=100 2>/dev/null
# Start BWS
./target/release/bws --config test-load-config.toml &
BWS_PID=$!
sleep 2
}
run_load_tests() {
echo "Running load tests..."
# Test 1: Basic load test
echo "=== Basic Load Test ==="
wrk -t4 -c50 -d30s --latency http://127.0.0.1:8080/
# Test 2: High concurrency
echo "=== High Concurrency Test ==="
wrk -t8 -c200 -d30s --latency http://127.0.0.1:8080/
# Test 3: Different file sizes
echo "=== 1KB File Test ==="
wrk -t4 -c50 -d15s http://127.0.0.1:8080/1kb.txt
echo "=== 10KB File Test ==="
wrk -t4 -c50 -d15s http://127.0.0.1:8080/10kb.txt
echo "=== 100KB File Test ==="
wrk -t4 -c50 -d15s http://127.0.0.1:8080/100kb.txt
# Test 4: Sustained load
echo "=== Sustained Load Test (5 minutes) ==="
wrk -t4 -c100 -d300s --latency http://127.0.0.1:8080/
}
cleanup_test() {
echo "Cleaning up..."
if [ ! -z "$BWS_PID" ]; then
kill $BWS_PID 2>/dev/null
fi
rm -rf load_test_static test-load-config.toml
}
# Trap cleanup on script exit
trap cleanup_test EXIT
setup_test_server
run_load_tests
Benchmark Tests
#![allow(unused)] fn main() { // benches/server_benchmark.rs use criterion::{black_box, criterion_group, criterion_main, Criterion}; use bws::{Config, Server}; fn bench_config_parsing(c: &mut Criterion) { let config_str = r#" [[sites]] name = "bench" hostname = "127.0.0.1" port = 8080 static_dir = "static" [sites.headers] "Cache-Control" = "public, max-age=3600" "X-Content-Type-Options" = "nosniff" "#; c.bench_function("parse config", |b| { b.iter(|| { let _config: Config = toml::from_str(black_box(config_str)).unwrap(); }) }); } fn bench_static_file_resolution(c: &mut Criterion) { let config = test_config(); c.bench_function("resolve static file", |b| { b.iter(|| { let _path = resolve_static_file( black_box("/assets/css/main.css"), black_box(&config.sites[0]) ); }) }); } criterion_group!(benches, bench_config_parsing, bench_static_file_resolution); criterion_main!(benches); }
Memory Testing
#![allow(unused)] fn main() { // tests/memory_tests.rs #[test] fn test_memory_usage() { use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; let memory_counter = Arc::new(AtomicUsize::new(0)); // Custom allocator to track memory usage #[global_allocator] static GLOBAL: TrackingAllocator = TrackingAllocator; let initial_memory = get_memory_usage(); // Create large number of configs let configs: Vec<Config> = (0..1000) .map(|i| Config { sites: vec![Site { name: format!("site_{}", i), hostname: "127.0.0.1".to_string(), port: 8080 + i, static_dir: format!("static_{}", i), ..Default::default() }], ..Default::default() }) .collect(); let peak_memory = get_memory_usage(); drop(configs); // Force garbage collection std::hint::black_box(()); let final_memory = get_memory_usage(); println!("Initial memory: {} KB", initial_memory / 1024); println!("Peak memory: {} KB", peak_memory / 1024); println!("Final memory: {} KB", final_memory / 1024); // Memory should be released assert!(final_memory < peak_memory); } }
Security Testing
Input Validation Tests
#![allow(unused)] fn main() { #[tokio::test] async fn test_path_traversal_protection() { // Test various path traversal attempts let malicious_paths = vec![ "../../../etc/passwd", "..%2F..%2F..%2Fetc%2Fpasswd", "....//....//....//etc//passwd", "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd", ]; for path in malicious_paths { let response = reqwest::get(&format!("http://127.0.0.1:8080/{}", path)) .await .unwrap(); // Should return 404 or 403, not 200 assert_ne!(response.status(), 200); } } #[tokio::test] async fn test_request_size_limits() { let client = reqwest::Client::new(); // Test large request body let large_body = "x".repeat(10 * 1024 * 1024); // 10MB let response = client .post("http://127.0.0.1:8080/") .body(large_body) .send() .await .unwrap(); // Should reject large requests assert_eq!(response.status(), 413); // Payload Too Large } #[tokio::test] async fn test_header_injection() { let client = reqwest::Client::new(); // Test header injection attempts let response = client .get("http://127.0.0.1:8080/") .header("X-Forwarded-For", "malicious\r\nContent-Type: text/html") .send() .await .unwrap(); // Response should not contain injected header assert!(!response.headers().contains_key("content-type")); } }
Rate Limiting Tests
#![allow(unused)] fn main() { #[tokio::test] async fn test_rate_limiting() { let client = reqwest::Client::new(); // Make requests rapidly let mut success_count = 0; let mut rate_limited_count = 0; for _ in 0..100 { let response = client .get("http://127.0.0.1:8080/") .send() .await .unwrap(); match response.status().as_u16() { 200 => success_count += 1, 429 => rate_limited_count += 1, // Too Many Requests _ => {} } } // Should have some rate limited responses assert!(rate_limited_count > 0); println!("Success: {}, Rate Limited: {}", success_count, rate_limited_count); } }
Test Utilities and Helpers
Test Configuration Factory
#![allow(unused)] fn main() { // tests/common/mod.rs pub fn test_config() -> Config { Config { daemon: DaemonConfig::default(), logging: LoggingConfig { level: "debug".to_string(), output: "stdout".to_string(), ..Default::default() }, sites: vec![Site { name: "test".to_string(), hostname: "127.0.0.1".to_string(), port: 8080, static_dir: "test_static".to_string(), ..Default::default() }], ..Default::default() } } pub fn test_config_with_port(port: u16) -> Config { let mut config = test_config(); config.sites[0].port = port; config } pub fn test_config_multi_site() -> Config { Config { sites: vec![ Site { name: "main".to_string(), hostname: "127.0.0.1".to_string(), port: 8080, static_dir: "main_static".to_string(), ..Default::default() }, Site { name: "api".to_string(), hostname: "127.0.0.1".to_string(), port: 8081, static_dir: "api_static".to_string(), ..Default::default() }, ], ..Default::default() } } }
Test Server Management
#![allow(unused)] fn main() { use std::sync::Once; use tokio::sync::Mutex; static INIT: Once = Once::new(); static TEST_SERVER: Mutex<Option<TestServer>> = Mutex::const_new(None); pub struct TestServer { pub port: u16, handle: tokio::task::JoinHandle<()>, } impl TestServer { pub async fn start(config: Config) -> Self { let port = config.sites[0].port; let server = Server::new(config).await.unwrap(); let handle = tokio::spawn(async move { server.run().await.unwrap(); }); // Wait for server to start tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; TestServer { port, handle } } pub fn url(&self) -> String { format!("http://127.0.0.1:{}", self.port) } } impl Drop for TestServer { fn drop(&mut self) { self.handle.abort(); } } // Global test server for shared tests pub async fn get_test_server() -> &'static TestServer { let mut server = TEST_SERVER.lock().await; if server.is_none() { *server = Some(TestServer::start(test_config()).await); } server.as_ref().unwrap() } }
Test File Management
#![allow(unused)] fn main() { use tempfile::{TempDir, NamedTempFile}; pub struct TestStaticFiles { pub temp_dir: TempDir, pub index_file: PathBuf, pub css_file: PathBuf, pub js_file: PathBuf, } impl TestStaticFiles { pub async fn new() -> Self { let temp_dir = TempDir::new().unwrap(); let index_file = temp_dir.path().join("index.html"); tokio::fs::write(&index_file, "<h1>Test Index</h1>").await.unwrap(); let css_file = temp_dir.path().join("style.css"); tokio::fs::write(&css_file, "body { color: red; }").await.unwrap(); let js_file = temp_dir.path().join("script.js"); tokio::fs::write(&js_file, "console.log('test');").await.unwrap(); TestStaticFiles { temp_dir, index_file, css_file, js_file, } } pub fn path(&self) -> &Path { self.temp_dir.path() } } }
Continuous Integration Testing
GitHub Actions Test Workflow
# .github/workflows/test.yml
name: Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
name: Test Suite
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
rust: [stable, beta]
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
components: rustfmt, clippy
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run tests
run: cargo test --all-features --verbose
- name: Run integration tests
run: cargo test --test integration --all-features
- name: Run benchmarks (check only)
run: cargo bench --no-run
Test Coverage
# Add to GitHub Actions
- name: Install coverage tools
run: |
cargo install cargo-tarpaulin
- name: Generate test coverage
run: |
cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./cobertura.xml
Testing Best Practices
Test Organization
- Group related tests in modules
- Use descriptive test names
- Follow AAA pattern (Arrange, Act, Assert)
- Test both happy path and error cases
- Use test fixtures for common setup
Test Data Management
- Use temporary directories for file operations
- Clean up resources in test teardown
- Use factories for creating test objects
- Avoid hardcoded values, use constants
Performance Testing
- Run performance tests in isolated environment
- Use consistent hardware for benchmarks
- Monitor for performance regressions
- Set reasonable performance thresholds
Security Testing
- Test all input validation
- Check authentication and authorization
- Verify secure defaults
- Test rate limiting and DOS protection
Test Maintenance
- Keep tests up to date with code changes
- Remove or update obsolete tests
- Refactor duplicated test code
- Document complex test scenarios
Next Steps
- Set up Continuous Integration
- Learn about Performance Tuning for optimization
- Review Contributing Guidelines for development workflow
- Check Troubleshooting for common issues
Troubleshooting
This guide helps you diagnose and resolve common issues with BWS installation, configuration, and operation.
Common Issues
Installation Problems
Rust Installation Issues
Problem: cargo command not found
cargo: command not found
Solution:
# Install Rust via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Or add to shell profile
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
Problem: Outdated Rust version
error: package `bws v0.1.5` cannot be built because it requires rustc 1.89 or newer
Solution:
# Update Rust toolchain
rustup update stable
rustc --version # Should show 1.89 or newer
Compilation Errors
Problem: Missing system dependencies
error: failed to run custom build command for `openssl-sys v0.9.xx`
Solution:
# Ubuntu/Debian
sudo apt update
sudo apt install -y pkg-config libssl-dev build-essential
# CentOS/RHEL/Fedora
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y pkg-config openssl-devel
# macOS
brew install openssl pkg-config
Problem: Linker errors
error: linking with `cc` failed: exit status: 1
note: /usr/bin/ld: cannot find -lssl
Solution:
# Ubuntu/Debian
sudo apt install -y libssl-dev
# Set environment variables if needed
export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig"
export OPENSSL_DIR="/usr"
Configuration Issues
Invalid TOML Syntax
Problem: Configuration file parsing errors
Error: failed to parse config file
Caused by: invalid TOML syntax at line 5, column 10
Solution:
# Check TOML syntax
# Common issues:
# 1. Missing quotes around strings
name = example # Wrong
name = "example" # Correct
# 2. Incorrect array syntax
ports = [8080 8081] # Wrong (missing comma)
ports = [8080, 8081] # Correct
# 3. Misplaced sections
[[sites]]
name = "test"
[daemon] # Wrong (should be before [[sites]])
# Validate TOML syntax online: https://www.toml-lint.com/
Missing Required Fields
Problem: Configuration validation errors
Error: missing required field `port` for site 'example'
Solution:
# Ensure all required fields are present
[[sites]]
name = "example" # Required
hostname = "localhost" # Required
port = 8080 # Required
static_dir = "static" # Required
Port Binding Issues
Problem: Address already in use
Error: failed to bind to 127.0.0.1:8080
Caused by: Address already in use (os error 98)
Solution:
# Check what's using the port
lsof -i :8080
netstat -tulpn | grep :8080
# Kill the process using the port
kill -9 $(lsof -ti:8080)
# Or use a different port
[[sites]]
name = "example"
hostname = "localhost"
port = 8081 # Use different port
static_dir = "static"
File Permission Issues
Problem: Cannot access static files
Error: Permission denied (os error 13)
Solution:
# Check file permissions
ls -la static/
# Fix permissions
chmod 755 static/
chmod 644 static/*
find static/ -type d -exec chmod 755 {} \;
find static/ -type f -exec chmod 644 {} \;
# Check directory ownership
sudo chown -R $USER:$USER static/
Runtime Issues
BWS Won't Start
Problem: Server fails to start silently
# No output, process exits immediately
Solution:
# Enable debug logging
RUST_LOG=debug ./target/release/bws --config config.toml
# Check configuration
./target/release/bws --config config.toml --validate
# Check system resources
df -h # Disk space
free -h # Memory
ulimit -n # File descriptors
High Memory Usage
Problem: BWS consuming excessive memory
# Process using > 1GB RAM for simple static serving
Solution:
# Monitor memory usage
ps aux | grep bws
top -p $(pgrep bws)
# Check for memory leaks
valgrind --tool=memcheck --leak-check=full ./target/release/bws
# Optimize configuration
[performance]
max_connections = 1000 # Reduce if too high
worker_threads = 4 # Match CPU cores
connection_pool_size = 100 # Reduce pool size
[caching]
max_memory = "100MB" # Limit cache size
High CPU Usage
Problem: BWS using 100% CPU
# CPU usage constantly high even with low traffic
Solution:
# Profile CPU usage
perf record -g ./target/release/bws --config config.toml
perf report
# Check configuration
[performance]
worker_threads = 4 # Don't exceed CPU cores
keep_alive_timeout = 30 # Reduce timeout
# Monitor system load
htop
iostat 1
Connection Issues
Problem: Cannot connect to BWS
curl: (7) Failed to connect to localhost port 8080: Connection refused
Solution:
# Check if BWS is running
ps aux | grep bws
systemctl status bws # If using systemd
# Check port binding
netstat -tulpn | grep :8080
ss -tulpn | grep :8080
# Check firewall
sudo ufw status
sudo iptables -L
# Test locally first
curl -v http://127.0.0.1:8080/
Hot Reload Issues
Hot Reload Not Working
Problem: Configuration changes not applied after SIGHUP
kill -HUP $(pgrep -f "bws.*master")
# No new worker spawned, configuration not updated
Solution:
# Check if master process exists
pgrep -f "bws.*master"
# Verify BWS is running in master-worker mode
ps aux | grep bws | grep -v grep
# Check logs for errors
tail -f /var/log/bws/bws.log | grep -E "(reload|error|worker|master)"
# Validate configuration before reload
bws --config-check /etc/bws/config.toml
# If master process not found, restart BWS
systemctl restart bws
Configuration Validation Failed
Problem: Invalid configuration preventing hot reload
tail -f /var/log/bws/bws.log
# ERROR: Configuration validation failed: ...
Solution:
# Test configuration syntax
bws --config-check /etc/bws/config.toml
# Common validation issues:
# - Invalid port numbers
# - Missing static directories
# - Invalid SSL certificate paths
# - Malformed TOML syntax
# Fix configuration and retry
vim /etc/bws/config.toml
bws --config-check /etc/bws/config.toml
systemctl reload bws
Worker Process Issues
Problem: New worker fails to start during reload
# Master spawns worker but worker exits immediately
Solution:
# Check worker process logs
journalctl -u bws -f | grep worker
# Common causes:
# 1. Port already in use by another process
netstat -tulpn | grep :8080
# 2. Permission issues
# Check file permissions for static directories
ls -la /var/www/html/
# 3. SSL certificate issues
# Verify certificate files exist and are readable
ls -la /etc/ssl/certs/example.com.crt
ls -la /etc/ssl/private/example.com.key
# 4. Resource limits
ulimit -n # Check file descriptor limit
Master Process Not Responding
Problem: Master process exists but doesn't respond to signals
pgrep -f "bws.*master" # Shows PID
kill -HUP <PID> # No response
Solution:
# Check if process is stuck
ps aux | grep bws
top -p $(pgrep -f "bws.*master")
# Check system resources
free -h
df -h
# Force restart if unresponsive
systemctl stop bws
sleep 5
systemctl start bws
# Check process tree after restart
pstree -p $(pgrep -f "bws.*master")
Configuration Not Updating
Problem: Hot reload succeeds but changes not visible
# No errors in logs, new worker spawned, but config unchanged
Solution:
# Verify configuration file is correct
cat /etc/bws/config.toml
# Check if BWS is reading the right config file
ps aux | grep bws | grep -o -- '--config [^ ]*'
# Test specific changes
curl -I http://localhost:8080/ | grep "X-Custom-Header"
# Check if browser is caching
curl -H "Cache-Control: no-cache" http://localhost:8080/
# Verify worker process PID changed
# Before reload: note worker PID
ps aux | grep bws
# After reload: verify PID is different
Performance Issues
Slow Response Times
Problem: High latency for static file serving
# Response times > 1 second for small files
Solution:
# Check disk I/O
iostat -x 1
iotop
# Optimize storage
# Use SSD for static files
# Enable file system caching
mount -o remount,noatime /path/to/static
# Tune BWS configuration
[performance]
read_buffer_size = "64KB" # Increase buffer size
write_buffer_size = "64KB"
worker_threads = 8 # Increase workers
[caching]
enabled = true
max_memory = "1GB" # Enable caching
Low Throughput
Problem: Cannot handle expected load
# Failing under moderate load (< 1000 req/s)
Solution:
# Increase system limits
# /etc/security/limits.conf
bws soft nofile 65536
bws hard nofile 65536
# /etc/sysctl.conf
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# Tune BWS
[performance]
max_connections = 10000
worker_threads = 16 # 2x CPU cores
keep_alive_timeout = 60
# Use load balancer for scaling
Network Issues
Timeout Errors
Problem: Requests timing out
curl: (28) Operation timed out after 30000 milliseconds
Solution:
# Check network connectivity
ping hostname
traceroute hostname
# Increase timeouts
[performance]
request_timeout = 60 # Increase from 30
response_timeout = 60
keep_alive_timeout = 120
# Check for network congestion
iftop
nethogs
DNS Resolution Issues
Problem: Cannot resolve hostname
curl: (6) Could not resolve host: example.com
Solution:
# Test DNS resolution
nslookup example.com
dig example.com
# Check /etc/hosts
grep example.com /etc/hosts
# Use IP address instead
[[sites]]
name = "example"
hostname = "192.168.1.100" # Use IP instead of hostname
port = 8080
static_dir = "static"
SSL/TLS Issues
Certificate Problems
Problem: SSL certificate errors
Error: SSL certificate verification failed
Solution:
# Check certificate validity
openssl x509 -in cert.pem -text -noout
openssl verify -CAfile ca.pem cert.pem
# Check certificate permissions
ls -la /etc/ssl/certs/
chmod 644 /etc/ssl/certs/cert.pem
chmod 600 /etc/ssl/private/key.pem
# Verify certificate chain
openssl s_client -connect example.com:443 -showcerts
TLS Handshake Failures
Problem: TLS handshake errors
Error: TLS handshake failed
Solution:
# Check TLS configuration
[sites.ssl]
enabled = true
protocols = ["TLSv1.2", "TLSv1.3"] # Ensure modern protocols
cert_file = "/path/to/cert.pem"
key_file = "/path/to/key.pem"
# Test TLS connection
openssl s_client -connect localhost:8443 -tls1_2
Docker Issues
Container Won't Start
Problem: Docker container exits immediately
docker run bws:latest
# Container exits with code 1
Solution:
# Check container logs
docker logs container_id
# Run interactively for debugging
docker run -it --entrypoint /bin/bash bws:latest
# Check file permissions in container
docker run --rm bws:latest ls -la /app/
# Mount configuration correctly
docker run -v $(pwd)/config.toml:/app/config.toml bws:latest
Volume Mount Issues
Problem: Cannot access mounted files
Error: No such file or directory: /app/static/index.html
Solution:
# Check volume mount syntax
docker run -v $(pwd)/static:/app/static bws:latest
# Verify host path exists
ls -la $(pwd)/static/
# Check file permissions
chmod -R 644 static/
chmod 755 static/
# Use absolute paths
docker run -v /full/path/to/static:/app/static bws:latest
Diagnostic Commands
System Information
#!/bin/bash
# diagnostic.sh - System diagnostic script
echo "=== BWS Diagnostic Information ==="
echo "Date: $(date)"
echo "Host: $(hostname)"
echo
echo "=== System Information ==="
uname -a
cat /etc/os-release 2>/dev/null || cat /etc/redhat-release 2>/dev/null
echo
echo "=== Resource Usage ==="
echo "CPU cores: $(nproc)"
echo "Memory:"
free -h
echo "Disk:"
df -h
echo "Load average:"
uptime
echo
echo "=== Network Configuration ==="
ip addr show
echo
netstat -tulpn | grep -E ":(8080|8081|8082|8083)"
echo
echo "=== BWS Process Information ==="
if pgrep -f bws > /dev/null; then
echo "BWS is running:"
ps aux | grep -v grep | grep bws
echo
echo "Open files:"
lsof -p $(pgrep bws) | head -20
else
echo "BWS is not running"
fi
echo
echo "=== Configuration Files ==="
find . -name "*.toml" -exec echo "File: {}" \; -exec head -20 {} \; -exec echo \;
echo "=== Log Files ==="
find /var/log -name "*bws*" 2>/dev/null | while read log; do
echo "=== $log ==="
tail -20 "$log" 2>/dev/null
echo
done
echo "=== Recent System Logs ==="
journalctl -u bws --no-pager -n 20 2>/dev/null || echo "No systemd logs found"
Network Diagnostics
#!/bin/bash
# network-test.sh - Network connectivity test
HOST=${1:-localhost}
PORT=${2:-8080}
echo "Testing connectivity to $HOST:$PORT"
# Test basic connectivity
echo "=== Ping Test ==="
ping -c 3 $HOST
echo "=== Port Test ==="
nc -zv $HOST $PORT 2>&1
echo "=== HTTP Test ==="
curl -v http://$HOST:$PORT/ 2>&1 | head -20
echo "=== DNS Resolution ==="
nslookup $HOST
echo "=== Route Trace ==="
traceroute $HOST 2>/dev/null | head -10
Performance Monitoring
#!/bin/bash
# monitor.sh - Real-time BWS monitoring
BWS_PID=$(pgrep bws)
if [ -z "$BWS_PID" ]; then
echo "BWS process not found"
exit 1
fi
echo "Monitoring BWS process $BWS_PID"
echo "Press Ctrl+C to stop"
while true; do
clear
echo "=== BWS Monitoring - $(date) ==="
echo
# Process information
echo "=== Process Information ==="
ps -o pid,ppid,cmd,%mem,%cpu,time -p $BWS_PID
echo
# Memory usage
echo "=== Memory Usage ==="
cat /proc/$BWS_PID/status | grep -E "VmSize|VmRSS|VmData|VmStk"
echo
# Network connections
echo "=== Network Connections ==="
ss -tuln | grep ":8080\|:8081\|:8082\|:8083" | wc -l | xargs echo "Active connections:"
echo
# File descriptors
echo "=== File Descriptors ==="
ls /proc/$BWS_PID/fd/ | wc -l | xargs echo "Open file descriptors:"
echo
# System load
echo "=== System Load ==="
uptime
sleep 5
done
Log Analysis
Error Log Patterns
# Common error patterns to look for
# Configuration errors
grep -i "config\|parse\|invalid" /var/log/bws/bws.log
# Network errors
grep -i "bind\|connection\|timeout" /var/log/bws/bws.log
# File system errors
grep -i "permission\|not found\|access" /var/log/bws/bws.log
# Performance issues
grep -i "slow\|timeout\|overload" /var/log/bws/bws.log
# Security issues
grep -i "attack\|malicious\|blocked" /var/log/bws/bws.log
Log Analysis Script
#!/bin/bash
# analyze-logs.sh - BWS log analysis
LOG_FILE=${1:-/var/log/bws/bws.log}
if [ ! -f "$LOG_FILE" ]; then
echo "Log file not found: $LOG_FILE"
exit 1
fi
echo "Analyzing BWS logs: $LOG_FILE"
echo "Log file size: $(du -h $LOG_FILE | cut -f1)"
echo "Total lines: $(wc -l < $LOG_FILE)"
echo
echo "=== Error Summary ==="
grep -i error "$LOG_FILE" | wc -l | xargs echo "Total errors:"
grep -i warn "$LOG_FILE" | wc -l | xargs echo "Total warnings:"
echo
echo "=== Recent Errors ==="
grep -i error "$LOG_FILE" | tail -10
echo
echo "=== Top Error Messages ==="
grep -i error "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo
echo "=== Request Statistics ==="
if grep -q "request" "$LOG_FILE"; then
grep "request" "$LOG_FILE" | wc -l | xargs echo "Total requests:"
# Status code distribution
echo "Status codes:"
grep -o '"status":[0-9]*' "$LOG_FILE" | cut -d: -f2 | sort | uniq -c | sort -rn
fi
Recovery Procedures
Service Recovery
#!/bin/bash
# recover-bws.sh - BWS service recovery
echo "Starting BWS recovery procedure..."
# Stop any running instances
echo "Stopping BWS..."
systemctl stop bws 2>/dev/null || pkill -f bws
# Clean up PID files
rm -f /var/run/bws.pid
# Check configuration
echo "Validating configuration..."
if ! bws --config /etc/bws/config.toml --validate; then
echo "Configuration invalid, please fix and retry"
exit 1
fi
# Check file permissions
echo "Checking file permissions..."
if [ ! -r /etc/bws/config.toml ]; then
echo "Configuration file not readable"
chmod 644 /etc/bws/config.toml
fi
# Check static directory
STATIC_DIR=$(grep static_dir /etc/bws/config.toml | head -1 | cut -d'"' -f2)
if [ ! -d "$STATIC_DIR" ]; then
echo "Creating static directory: $STATIC_DIR"
mkdir -p "$STATIC_DIR"
echo "<h1>BWS Recovery Page</h1>" > "$STATIC_DIR/index.html"
fi
# Start service
echo "Starting BWS..."
if systemctl start bws; then
echo "BWS started successfully"
systemctl status bws
else
echo "Failed to start BWS, check logs:"
journalctl -u bws --no-pager -n 20
fi
Database Recovery (if applicable)
#!/bin/bash
# recover-database.sh - Database recovery for BWS
echo "Database recovery procedure..."
# Check database connection
if ! pg_isready -h localhost -p 5432; then
echo "Database not accessible"
exit 1
fi
# Backup current database
pg_dump bws > /backup/bws-recovery-$(date +%Y%m%d).sql
# Run database maintenance
psql bws -c "VACUUM ANALYZE;"
psql bws -c "REINDEX DATABASE bws;"
echo "Database recovery completed"
Prevention Strategies
Monitoring Setup
# Set up basic monitoring
crontab -e
# Add monitoring jobs
*/5 * * * * /usr/local/bin/bws-health-check || /usr/bin/logger "BWS health check failed"
0 */6 * * * /usr/local/bin/cleanup-logs
0 2 * * * /usr/local/bin/backup-bws-config
Automated Backups
#!/bin/bash
# backup-bws.sh - Automated BWS backup
BACKUP_DIR="/backup/bws"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
# Backup configuration
tar -czf "$BACKUP_DIR/config-$DATE.tar.gz" /etc/bws/
# Backup static files
tar -czf "$BACKUP_DIR/static-$DATE.tar.gz" /var/www/
# Backup logs
tar -czf "$BACKUP_DIR/logs-$DATE.tar.gz" /var/log/bws/
# Clean old backups (keep 30 days)
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR"
Health Monitoring
#!/bin/bash
# health-monitor.sh - Continuous health monitoring
ALERT_EMAIL="admin@example.com"
ALERT_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
check_health() {
if ! curl -f -s http://localhost:8080/health > /dev/null; then
return 1
fi
return 0
}
send_alert() {
local message="$1"
# Email alert
echo "$message" | mail -s "BWS Alert" "$ALERT_EMAIL"
# Slack alert
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\"}" \
"$ALERT_WEBHOOK"
}
# Main monitoring loop
while true; do
if ! check_health; then
send_alert "BWS health check failed at $(date)"
# Attempt recovery
systemctl restart bws
sleep 30
if check_health; then
send_alert "BWS recovered successfully at $(date)"
else
send_alert "BWS recovery failed - manual intervention required"
fi
fi
sleep 60
done
Getting Help
Information to Collect
When reporting issues, include:
-
System Information
- Operating system and version
- Rust version (
rustc --version) - BWS version (
bws --version)
-
Configuration
- Configuration file content (sanitized)
- Command line arguments used
-
Error Information
- Complete error messages
- Stack traces if available
- Log file contents
-
Environment
- Available memory and disk space
- Network configuration
- Other running services
Support Channels
- GitHub Issues: Report bugs and request features
- GitHub Discussions: Ask questions and get help
- Documentation: Check existing documentation
- Community: Join discussions with other users
Before Reporting
- Search existing issues
- Try the latest version
- Check documentation
- Provide minimal reproduction case
- Include diagnostic information
Next Steps
After resolving issues:
- Update your monitoring setup
- Review Performance Tuning
- Check Security Best Practices
- Consider High Availability Setup
Frequently Asked Questions (FAQ)
General Questions
What is BWS?
BWS (Basic Web Server) is a high-performance, multi-site web server built with Rust and powered by Cloudflare's Pingora framework. It's designed for serving static files and provides enterprise-grade features including multi-site hosting, SSL/TLS support, and comprehensive monitoring.
Why use BWS over other web servers?
Performance: Built on Pingora, BWS offers exceptional performance and low latency.
Safety: Written in Rust, BWS provides memory safety and prevents common security vulnerabilities.
Simplicity: Easy configuration through TOML files and straightforward deployment.
Multi-tenancy: Native support for hosting multiple sites from a single instance.
Modern Features: Built-in support for WASM, comprehensive MIME types, and modern web standards.
Can I quickly serve a directory without configuration?
Yes! BWS supports instant directory serving. Simply run:
# Serve current directory
bws .
# Serve a specific directory
bws /path/to/your/files
# On Windows
bws C:\MyWebsite
This bypasses the need for configuration files and instantly starts serving static files on http://localhost:8080.
What are the system requirements?
Minimum Requirements:
- CPU: 1 core (x86_64 or ARM64)
- RAM: 256MB
- Storage: 100MB for binary + static content
- OS: Linux, macOS, or Windows
Recommended for Production:
- CPU: 4+ cores
- RAM: 2GB+
- Storage: SSD with sufficient space for content
- OS: Linux (Ubuntu 20.04+, CentOS 8+, RHEL 8+)
Is BWS production-ready?
Yes, BWS is production-ready. It's built on Cloudflare's battle-tested Pingora framework and includes:
- Comprehensive error handling
- Security hardening
- Performance optimization
- Monitoring and logging
- Automated testing
- Documentation and support
Installation & Setup
How do I install BWS?
You have several options:
From pre-built binaries:
# Download from GitHub releases
wget https://github.com/yourusername/bws/releases/latest/download/bws-linux-x86_64.tar.gz
tar -xzf bws-linux-x86_64.tar.gz
From source:
git clone https://github.com/yourusername/bws.git
cd bws
cargo build --release
Using Docker:
docker pull ghcr.io/yourusername/bws:latest
See the Installation Guide for detailed instructions.
Do I need root privileges to run BWS?
No, BWS can run as a non-root user. However:
- Ports < 1024: Require root privileges or
CAP_NET_BIND_SERVICEcapability - File access: Ensure the user can read configuration and static files
- Log files: Ensure the user can write to log directories
Best practice: Run as a dedicated user with minimal privileges:
useradd -r -s /bin/false bws
sudo -u bws ./bws --config config.toml
Can I run BWS on Windows?
Yes, BWS supports Windows. However, some features may behave differently:
- Use Windows paths (
C:\path o\files) - Service management differs from Linux
- Performance may vary compared to Linux
For production, Linux is recommended.
Configuration
How do I configure multiple sites?
Use the [[sites]] array in your configuration:
[[sites]]
name = "main"
hostname = "example.com"
port = 8080
static_dir = "/var/www/main"
[[sites]]
name = "blog"
hostname = "blog.example.com"
port = 8080
static_dir = "/var/www/blog"
[[sites]]
name = "api"
hostname = "api.example.com"
port = 8081
static_dir = "/var/www/api"
Each site can have different configurations while sharing the same BWS instance.
Can I use environment variables in configuration?
Currently, BWS doesn't support environment variable substitution in TOML files. However, you can:
- Generate configuration programmatically:
envsubst < config.template.toml > config.toml
- Use multiple config files:
bws --config config-${ENVIRONMENT}.toml
- Override specific settings via command line (if supported in your version)
How do I enable SSL/TLS?
Add SSL configuration to your site:
[[sites]]
name = "secure"
hostname = "example.com"
port = 8443
static_dir = "/var/www/secure"
[sites.ssl]
enabled = true
cert_file = "/etc/ssl/certs/example.com.pem"
key_file = "/etc/ssl/private/example.com.key"
protocols = ["TLSv1.2", "TLSv1.3"]
See SSL/TLS Configuration for details.
How does hot reload work?
BWS uses a master-worker architecture for true hot reload:
- Master Process: Monitors configuration and manages workers
- Worker Processes: Handle HTTP requests and connections
- Hot Reload: Master spawns new workers with updated config
- Zero Downtime: Old workers finish existing requests gracefully
Trigger hot reload with:
# Send SIGHUP to master process
kill -HUP $(pgrep -f "bws.*master")
# Or using systemctl
systemctl reload bws
What can be hot reloaded?
โ Supported:
- Site configurations and hostnames
- SSL certificate paths
- Proxy upstream configurations
- Static file directories
- Security headers
- Load balancing settings
โ Requires restart:
- Server listen ports
- Core server settings
How do I check if hot reload succeeded?
Monitor the process:
# Check process tree
pstree -p $(pgrep -f "bws.*master")
# Watch logs
tail -f /var/log/bws/bws.log | grep -E "(reload|worker|master)"
# Test configuration
curl http://localhost:8080/health
What's the difference between hostname and domain?
- hostname: The network interface and host header to match
- domain: An alias for hostname (legacy compatibility)
Both fields serve the same purpose. Use hostname for new configurations.
How do I serve files from subdirectories?
BWS automatically serves files from subdirectories. For example, with:
static_dir = "/var/www/html"
These URLs work automatically:
http://example.com/โ/var/www/html/index.htmlhttp://example.com/about/โ/var/www/html/about/index.htmlhttp://example.com/assets/style.cssโ/var/www/html/assets/style.css
Performance
How many concurrent connections can BWS handle?
BWS can handle thousands of concurrent connections. The exact number depends on:
- System resources: CPU cores, RAM, file descriptors
- Configuration:
max_connections,worker_threads - Content type: Static files vs. dynamic content
- Network conditions: Latency, bandwidth
Typical performance:
- Small VPS (2 cores, 2GB RAM): 1,000-5,000 concurrent connections
- Medium server (8 cores, 16GB RAM): 10,000-50,000 concurrent connections
- Large server (32 cores, 64GB RAM): 100,000+ concurrent connections
How do I optimize BWS performance?
Configuration tuning:
[performance]
worker_threads = 8 # Match CPU cores * 2
max_connections = 10000 # Based on system capacity
read_buffer_size = "64KB" # Larger for big files
write_buffer_size = "64KB" # Larger for throughput
[caching]
enabled = true
max_memory = "1GB" # Adjust based on available RAM
System tuning:
# Increase file descriptor limits
echo "bws soft nofile 65536" >> /etc/security/limits.conf
echo "bws hard nofile 65536" >> /etc/security/limits.conf
# Tune kernel parameters
echo "net.core.somaxconn = 65535" >> /etc/sysctl.conf
echo "net.ipv4.tcp_max_syn_backlog = 65535" >> /etc/sysctl.conf
sysctl -p
See Performance Tuning for comprehensive optimization.
Does BWS support caching?
Yes, BWS includes built-in caching:
[caching]
enabled = true
max_memory = "512MB" # Memory limit for cache
ttl = 3600 # Time-to-live in seconds
compression = true # Enable gzip compression
Caching improves performance by:
- Reducing disk I/O
- Enabling content compression
- Serving frequently requested files from memory
Can BWS serve WASM files?
Yes, BWS includes native WASM support with proper MIME types:
.wasmfiles are served withapplication/wasm- Supports WebAssembly streaming compilation
- Enables modern web applications
No additional configuration required - WASM support is built-in.
Security
Is BWS secure?
BWS implements multiple security layers:
Language Safety: Written in Rust, preventing memory safety vulnerabilities.
Input Validation: Strict validation of requests and configuration.
Path Traversal Protection: Prevents access to files outside static directories.
Security Headers: Configurable security headers.
Regular Updates: Dependencies are regularly updated for security fixes.
How do I secure BWS in production?
Basic security:
[security]
hide_server_header = true # Don't reveal server information
max_request_size = "10MB" # Prevent large request attacks
[headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
Additional measures:
- Run as non-root user
- Use firewall to restrict access
- Enable SSL/TLS with strong ciphers
- Regular security updates
- Monitor logs for suspicious activity
See Security Best Practices for comprehensive guidance.
Does BWS log sensitive information?
BWS is designed to avoid logging sensitive information:
- Passwords and API keys are not logged
- Request bodies are not logged by default
- IP addresses are logged for legitimate monitoring
Log configuration:
[logging]
level = "info" # Avoid "debug" in production
access_log = "/var/log/bws/access.log"
error_log = "/var/log/bws/error.log"
Always review logs before sharing for troubleshooting.
Can I restrict access to certain files?
Currently, BWS serves all files from the static directory. For access control:
File system permissions:
# Remove read permissions for sensitive files
chmod 600 sensitive-file.txt
Reverse proxy approach: Use nginx or Apache as a reverse proxy for advanced access control:
location /admin/ {
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:8080;
}
Separate directories: Use different BWS sites for different access levels.
Deployment
How do I deploy BWS in production?
Systemd service (recommended for Linux):
# Create service file
sudo tee /etc/systemd/system/bws.service > /dev/null <<EOF
[Unit]
Description=BWS Web Server
After=network.target
[Service]
Type=simple
User=bws
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# Enable and start service
sudo systemctl enable bws
sudo systemctl start bws
Docker deployment:
docker run -d
--name bws
-p 8080:8080
-v /path/to/config.toml:/app/config.toml:ro
-v /path/to/static:/app/static:ro
ghcr.io/yourusername/bws:latest
See Production Deployment for complete instructions.
Can I use BWS with a reverse proxy?
Yes, BWS works well behind reverse proxies:
Nginx example:
upstream bws {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://bws;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Benefits:
- SSL termination at proxy
- Load balancing across multiple BWS instances
- Advanced routing and caching
- Security filtering
How do I update BWS?
Binary updates:
# Stop service
sudo systemctl stop bws
# Backup current binary
cp /usr/local/bin/bws /usr/local/bin/bws.backup
# Replace binary
wget https://github.com/yourusername/bws/releases/latest/download/bws-linux-x86_64.tar.gz
tar -xzf bws-linux-x86_64.tar.gz
sudo cp bws /usr/local/bin/
# Restart service
sudo systemctl start bws
Docker updates:
# Pull new image
docker pull ghcr.io/yourusername/bws:latest
# Restart with new image
docker-compose down
docker-compose up -d
Source updates:
git pull origin main
cargo build --release
sudo cp target/release/bws /usr/local/bin/
sudo systemctl restart bws
Can I run multiple BWS instances?
Yes, you can run multiple BWS instances:
Same machine, different ports:
# Instance 1: config1.toml
[[sites]]
name = "site1"
hostname = "localhost"
port = 8080
static_dir = "/var/www/site1"
# Instance 2: config2.toml
[[sites]]
name = "site2"
hostname = "localhost"
port = 8081
static_dir = "/var/www/site2"
Load balancing: Use a load balancer to distribute traffic across instances.
Different machines: Deploy separate BWS instances on different servers for horizontal scaling.
Troubleshooting
BWS directory serving doesn't work - what to check?
When using bws [path] for instant directory serving:
- Directory doesn't exist:
# Verify the path exists
ls -la /path/to/directory # Linux/macOS
dir C:\path\to\directory # Windows
- Permission issues:
# Check read permissions
ls -la /path/to/directory
# Ensure BWS can read the directory contents
- Port already in use:
# Default port 8080 might be occupied
netstat -an | grep :8080 # Linux/macOS
netstat -an | findstr :8080 # Windows
- Windows path issues:
- Use forward slashes:
bws C:/MyWebsite - Or escape backslashes:
bws "C:\\MyWebsite" - BWS automatically handles Windows extended paths
BWS won't start - what should I check?
Common issues:
- Configuration errors:
bws --config config.toml --validate
- Port already in use:
lsof -i :8080
- File permissions:
ls -la config.toml
ls -la /path/to/static/
- Missing dependencies:
ldd /usr/local/bin/bws # Linux
otool -L /usr/local/bin/bws # macOS
See Troubleshooting Guide for comprehensive diagnostics.
High memory usage - is this normal?
Memory usage depends on:
Configuration:
- Cache size (
max_memory) - Worker threads (
worker_threads) - Connection pool size
Workload:
- Number of concurrent connections
- File sizes being served
- Request frequency
Normal ranges:
- Minimal configuration: 10-50MB
- Production configuration: 100-500MB
- Heavy caching: 1GB+
Monitor with:
ps aux | grep bws
top -p $(pgrep bws)
Request timeouts - how to fix?
Increase timeouts:
[performance]
request_timeout = 60 # Default: 30 seconds
response_timeout = 60 # Default: 30 seconds
keep_alive_timeout = 120 # Default: 60 seconds
Check system resources:
iostat -x 1 # Disk I/O
free -h # Memory usage
top # CPU usage
Network diagnostics:
curl -w "@curl-format.txt" -o /dev/null -s http://localhost:8080/
Where curl-format.txt contains:
time_namelookup: %{time_namelookup}
time_connect: %{time_connect}
time_pretransfer: %{time_pretransfer}
time_redirect: %{time_redirect}
time_starttransfer: %{time_starttransfer}
time_total: %{time_total}
Development
How can I contribute to BWS?
We welcome contributions! Here's how to get started:
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/yourusername/bws.git - Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes and add tests
- Run tests:
cargo test - Submit a pull request
See Contributing Guide for detailed instructions.
How do I build BWS from source?
Prerequisites:
# Install Rust (version 1.89+)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install system dependencies (Ubuntu/Debian)
sudo apt install -y pkg-config libssl-dev build-essential
Build process:
git clone https://github.com/yourusername/bws.git
cd bws
cargo build --release
# Binary will be at target/release/bws
See Building from Source for platform-specific instructions.
Can I extend BWS with custom features?
BWS is designed to be extensible. Common extension points:
Custom MIME types:
#![allow(unused)] fn main() { // Add to src/lib.rs pub fn get_mime_type(path: &str) -> &'static str { match path.split('.').last() { Some("myext") => "application/x-myformat", _ => get_default_mime_type(path), } } }
Custom headers:
[headers]
"X-Custom-Header" = "Custom Value"
"Cache-Control" = "max-age=3600"
Middleware integration: BWS is built on Pingora, which supports middleware. Consider contributing middleware to the main project.
How do I report bugs?
Before reporting:
- Check existing issues on GitHub
- Try the latest version
- Read the documentation
- Gather diagnostic information
Bug report should include:
- BWS version (
bws --version) - Operating system and version
- Configuration file (sanitized)
- Complete error messages
- Steps to reproduce
- Expected vs. actual behavior
Submit issues at: https://github.com/yourusername/bws/issues
License & Support
What license is BWS under?
BWS is released under the MIT License, which allows:
- Commercial use
- Modification
- Distribution
- Private use
With requirements for:
- Including the license notice
- Including the copyright notice
Where can I get support?
Free support:
- GitHub Issues (bug reports)
- GitHub Discussions (questions)
- Documentation (comprehensive guides)
- Community forums
Commercial support: Contact us for:
- Priority support
- Custom development
- Training and consulting
- Enterprise licensing
How often is BWS updated?
Regular releases:
- Patch releases: Monthly (bug fixes)
- Minor releases: Quarterly (new features)
- Major releases: Yearly (breaking changes)
Security updates:
- Critical security fixes: Within 24-48 hours
- Regular security updates: Weekly
Dependencies:
- Rust toolchain: Follow Rust stable releases
- Pingora framework: Updated with upstream releases
Stay updated by:
- Watching the GitHub repository
- Following release notes
- Subscribing to security advisories
Migration
Migrating from Apache/Nginx?
Configuration mapping:
Apache .htaccess โ BWS configuration:
# Apache
DocumentRoot /var/www/html
Listen 80
# BWS
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "/var/www/html"
Common features:
- Virtual hosts โ Multiple sites
- SSL configuration โ SSL section
- Custom headers โ Headers section
- Access logs โ Logging configuration
Migrating from other Rust web servers?
From Actix-web:
- Replace route handlers with static file serving
- Migrate middleware to configuration
- Update deployment scripts
From Warp:
- Convert filters to BWS configuration
- Replace custom handlers with static serving
- Update error handling
From Rocket:
- Replace routes with static file configuration
- Migrate state management to external systems
- Update launch configuration
Migrating configuration formats?
From JSON:
# Convert JSON to TOML
pip install json2toml
json2toml config.json config.toml
From YAML:
# Convert YAML to TOML
pip install yq
yq -t config.yaml > config.toml
Manual migration: Review and adapt configuration according to BWS schema.
Future Roadmap
What's planned for future versions?
Short-term (next 3 months):
- Enhanced monitoring and metrics
- Additional security features
- Performance optimizations
- Configuration validation improvements
Medium-term (next 6 months):
- Plugin system
- Advanced caching strategies
- HTTP/3 support
- Enhanced SSL/TLS configuration
Long-term (next year):
- GUI configuration interface
- Distributed deployment support
- Advanced load balancing
- Enterprise features
How can I influence the roadmap?
- Submit feature requests on GitHub
- Participate in discussions
- Contribute code
- Sponsor development
- Provide feedback and use cases
Don't see your question? Ask on GitHub Discussions or check the documentation.
Changelog
All notable changes to BWS will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.3.4] - 2025-08-26
Added
- ๐ Configuration Validation: Comprehensive
--dry-runfeature for validating configurations without starting server - ๐ Enhanced API Endpoints: Added
/api/health/detailedand/api/sitesendpoints for better monitoring - ๐ Hot Reload API: Added
/api/reloadendpoint for real-time configuration reloading - ๐ Secure Management API: Localhost-only Management API on port 7654 for administrative operations
- Restricted to
127.0.0.1binding for security - Optional API key authentication
- Comprehensive audit logging with client IP tracking
- Configuration reload endpoint:
POST /api/config/reload
- Restricted to
- ๐งช Professional Test Suite: Organized test scripts in
tests/scripts/directory with comprehensive coverage - ๐ Enhanced Documentation: Updated all documentation to reflect current codebase and new features
- โ๏ธ Simplified Architecture: Removed deprecated
bws-ctlbinary, focusing on singlebwsbinary - ๐ฏ GitHub Actions Integration: Professional CI/CD workflows with comprehensive testing and validation
Changed
- ๐ง Configuration Schema: Enhanced validation with detailed error reporting and warnings
- ๐ Test Organization: Reorganized test infrastructure with proper script categorization
- ๐๏ธ Build Process: Streamlined build process focusing on single binary output
- ๐ Documentation Structure: Comprehensive documentation updates reflecting current features
Removed
- โ bws-ctl Binary: Deprecated control binary, functionality integrated into main
bwsbinary - ๐งน Legacy Code: Removed unused and deprecated code paths
- ๐ Outdated Tests: Cleaned up redundant and non-functional test files
Fixed
- โ Configuration Validation: Robust error handling and validation throughout configuration system
- ๐ง Script Permissions: All test scripts now have proper executable permissions
- ๐ API Consistency: Standardized API response formats and error handling
[Unreleased]
Added
- ๏ฟฝ True Hot Reload: Master-worker architecture for zero-downtime configuration updates
- ๐๏ธ Master-Worker Process Model: Production-grade process management with worker lifecycle control
- โก Zero-Downtime Operations: Configuration and binary updates without dropping connections
- ๐ Graceful Worker Replacement: Spawn new workers while old workers finish existing requests
- ๐ Process Monitoring: Comprehensive process tree monitoring and management
- ๏ฟฝ๐ก๏ธ Production-Grade Error Handling: Comprehensive error handling throughout codebase - no more .unwrap() calls
- ๐ Automatic SSL Certificate Monitoring: Background certificate renewal service with robust error handling
- ๐งน Code Quality Improvements: Zero Clippy warnings achieved for maximum code quality
- ๐ง Thread-Safe SSL Operations: Fixed critical concurrency issues in certificate management
- ๐ Enhanced Documentation: Updated documentation reflecting improved robustness and reliability
- ๏ฟฝ WebSocket Proxy Support: Full WebSocket proxying framework with automatic upgrade detection
- โ๏ธ WebSocket Load Balancing: All load balancing algorithms extended to WebSocket connections
- ๐ Protocol Transformation: Automatic HTTP to WebSocket URL conversion (httpโws, httpsโwss)
- ๐ค Bidirectional Framework: Foundation for real-time message forwarding (streaming pending)
- ๐งช WebSocket Testing: Comprehensive test suite and interactive test script
- ๐ WebSocket Documentation: Complete documentation with examples and configuration guides
- ๏ฟฝ๐ Reverse Proxy Functionality: Complete Caddy-style reverse proxy implementation
- โ๏ธ Load Balancing: Three algorithms - round-robin, weighted, and least-connections
- ๐ Connection Tracking: Real-time connection monitoring for least-connections algorithm
- ๐ท๏ธ Header Management: Advanced proxy header forwarding and customization
- โฑ๏ธ Request Timeouts: Configurable read/write timeouts for proxy requests
- ๐ฃ๏ธ Path Transformation: URL rewriting and prefix stripping capabilities
- ๐ง Per-Site Proxy Config: Individual proxy configuration for each site
- ๐งช Load Balancing Tests: Comprehensive test suite for all load balancing methods
- ๐ Proxy Documentation: Detailed documentation for reverse proxy and load balancing
- Comprehensive documentation with mdBook
- Advanced troubleshooting guides
- Performance monitoring scripts
- Security hardening guidelines
Changed
- ๐ Enhanced Reliability: Replaced all dangerous .unwrap() calls with proper error handling
- ๐ SSL Manager Improvements: Fixed critical concurrency issues (Future not Send) in certificate operations
- โก Async Function Optimization: Improved async/await patterns throughout codebase
- ๐ Modern String Formatting: Updated 50+ format strings to use modern interpolation
- ๐ก๏ธ Thread-Safe Operations: All operations use proper atomic operations for concurrency
- Enhanced Service Architecture: Integrated proxy handler with main web service
- Thread-Safe Operations: All load balancing uses atomic operations for concurrency
- Configuration Schema: Extended TOML schema to support proxy configurations
- Improved error messages and diagnostics
- Enhanced configuration validation
- Better logging format and structure
Fixed
- ๐จ Critical SSL Concurrency Issues: Resolved "Future not Send" problems in SSL manager
- ๐ก๏ธ Race Condition Elimination: Fixed race conditions in certificate validation and load balancing
- ๐พ Resource Leak Prevention: Proper cleanup of certificate operations and connections
- โก Async Function Signatures: Fixed async/await patterns and removed unnecessary async functions
- ๐ง Iterator and Formatting Issues: Resolved compilation errors from iterator usage
- ๐ Error Documentation: Added comprehensive documentation for Result-returning functions
- Proxy Error Handling: Graceful fallback with 502 Bad Gateway responses
- Connection Cleanup: Proper connection tracking cleanup on request completion
- Concurrent Safety: Race condition fixes in load balancing counters
- Minor memory leaks in connection handling
- Edge cases in path resolution
- Configuration parsing edge cases
[0.1.5] - 2024-12-19
Added
- WASM (WebAssembly) MIME type support (
application/wasm) - Enhanced subdirectory file serving capabilities
- Comprehensive error handling and logging
- Security improvements and hardening
- Performance optimizations
- Docker containerization with multi-stage builds
- GitHub Actions CI/CD pipeline with automated testing
- Automated release workflow with GitHub Releases
- Container registry publishing (GitHub Container Registry)
- Supply chain security with attestations
- Code coverage reporting and quality gates
- Dependency vulnerability scanning
Changed
- Improved MIME type detection and handling
- Enhanced static file serving performance
- Better error messages and user feedback
- Upgraded to latest Pingora framework version
- Optimized binary size and runtime performance
Removed
- BREAKING: Removed
/api/fileroute for security reasons - Legacy file access API endpoints
- Deprecated configuration options
Fixed
- Path traversal security vulnerabilities
- Memory usage optimization in file serving
- Connection handling edge cases
- Configuration validation issues
- Build system improvements
Security
- Removed insecure file access endpoints
- Enhanced input validation
- Improved error handling to prevent information disclosure
- Added security headers by default
- Path sanitization improvements
[0.1.4] - 2024-12-15
Added
- Multi-site configuration support
- SSL/TLS termination capabilities
- Custom HTTP headers configuration
- Configurable logging levels and formats
- Performance tuning options
- Connection pooling and management
Changed
- Refactored configuration system for better flexibility
- Improved error handling and recovery
- Enhanced monitoring and observability features
- Better resource management and cleanup
Fixed
- Memory leaks in long-running connections
- Configuration reload handling
- Signal handling improvements
- Cross-platform compatibility issues
[0.1.3] - 2024-12-10
Added
- Comprehensive MIME type support for modern web assets
- Static file caching mechanisms
- Request/response logging with customizable formats
- Health check endpoints for monitoring
- Graceful shutdown handling
- Configuration file validation
Changed
- Improved startup time and resource initialization
- Better error propagation and handling
- Enhanced configuration file format
- More detailed logging and debugging information
Fixed
- File descriptor leaks
- Race conditions in multi-threaded operations
- Memory usage optimization
- Cross-platform path handling
[0.1.2] - 2024-12-05
Added
- Virtual host support for multiple domains
- Custom error page configuration
- Request rate limiting capabilities
- Basic authentication support
- Compression support (gzip, deflate)
- IPv6 support
Changed
- Modular architecture for better maintainability
- Improved configuration parsing and validation
- Better performance under high load
- Enhanced security measures
Fixed
- Buffer overflow in request parsing
- Deadlock issues in connection handling
- Memory fragmentation problems
- Platform-specific compilation issues
[0.1.1] - 2024-11-30
Added
- Basic HTTP/1.1 server functionality
- Static file serving with directory indexing
- Configuration file support (TOML format)
- Basic logging and error handling
- Signal handling for graceful shutdown
- Process daemonization support
Changed
- Improved code organization and modularity
- Better error messages and user feedback
- Enhanced configuration options
- Performance optimizations
Fixed
- File permission handling
- Memory management issues
- Connection timeout problems
- Configuration parsing edge cases
Security
- Input validation improvements
- Path traversal protection
- Basic security headers
[0.1.0] - 2024-11-25
Added
- Initial release of BWS (Basic Web Server)
- Core HTTP server functionality powered by Pingora
- Basic static file serving capabilities
- Simple configuration system
- Command-line interface
- Basic error handling and logging
- Cross-platform support (Linux, macOS, Windows)
- MIT license
Features
- High-performance HTTP server based on Cloudflare's Pingora framework
- Static file serving with automatic MIME type detection
- Configurable via TOML configuration files
- Multi-platform support
- Memory-safe implementation in Rust
- Lightweight and fast startup
- Basic security features
Technical Details
- Built with Rust 1.89+
- Uses Pingora 0.6.0 framework
- Supports HTTP/1.1 protocol
- Asynchronous I/O with Tokio runtime
- Comprehensive error handling
- Structured logging support
Release Process
Version Numbering
BWS follows Semantic Versioning:
- MAJOR version when making incompatible API changes
- MINOR version when adding functionality in a backwards compatible manner
- PATCH version when making backwards compatible bug fixes
Release Types
Major Releases (X.0.0):
- Breaking changes to configuration format
- Major architectural changes
- Removal of deprecated features
- Significant API changes
Minor Releases (X.Y.0):
- New features and capabilities
- Performance improvements
- Non-breaking configuration additions
- New platform support
Patch Releases (X.Y.Z):
- Bug fixes and security patches
- Documentation improvements
- Performance optimizations
- Dependency updates
Release Schedule
Regular Releases:
- Patch releases: Monthly or as needed for critical fixes
- Minor releases: Quarterly with new features
- Major releases: Annually or when breaking changes are necessary
Security Releases:
- Critical security fixes: Within 24-48 hours
- Regular security updates: As part of monthly patch releases
- Coordinated vulnerability disclosure: Following responsible disclosure practices
Release Artifacts
Each release includes:
Binary Distributions:
- Linux (x86_64, ARM64)
- macOS (Intel, Apple Silicon)
- Windows (x86_64)
Container Images:
- Docker images for multiple architectures
- Published to GitHub Container Registry
- Tagged with version numbers and
latest
Source Code:
- Tagged releases on GitHub
- Source code archives (tar.gz, zip)
- Build instructions and dependencies
Documentation:
- Release notes and changelog
- Updated documentation for new features
- Migration guides for breaking changes
Upgrade Guidelines
Before Upgrading:
- Read the changelog and release notes
- Check for breaking changes
- Backup configuration files
- Test in a non-production environment
Minor Version Upgrades:
- Generally safe with no configuration changes required
- New features available but not enabled by default
- Performance improvements included
Major Version Upgrades:
- May require configuration file updates
- Review breaking changes carefully
- Follow migration guides
- Test thoroughly before production deployment
Patch Version Upgrades:
- Safe to deploy immediately
- Include bug fixes and security patches
- No configuration changes required
Support Policy
Active Support:
- Latest major version: Full support with new features and fixes
- Previous major version: Security fixes and critical bug fixes for 12 months
- Older versions: Community support only
End of Life:
- Announced 6 months before end of support
- Final security update provided
- Migration path documented
Contributing to Releases
Bug Reports:
- Report issues on GitHub Issues
- Include version information and reproduction steps
- Security issues should be reported privately
Feature Requests:
- Discuss on GitHub Discussions
- Provide use cases and requirements
- Consider contributing implementation
Testing:
- Test release candidates and beta versions
- Provide feedback on performance and compatibility
- Report any regressions or issues
Release Notes Format
Each release includes:
Summary:
- High-level overview of changes
- Key features and improvements
- Breaking changes and migration notes
Detailed Changes:
- Added features and capabilities
- Changed behavior and improvements
- Deprecated features and migration path
- Removed features and alternatives
- Fixed bugs and issues
- Security improvements and patches
Technical Details:
- Dependencies updated
- Performance improvements
- Platform-specific changes
- Build system updates
Upgrade Instructions:
- Step-by-step upgrade process
- Configuration changes required
- Testing recommendations
- Rollback procedures
Historical Context
Project Milestones
November 2024: BWS project inception
- Initial concept and planning
- Technology stack selection (Rust + Pingora)
- Core architecture design
December 2024: First stable release (0.1.0)
- Basic HTTP server functionality
- Static file serving
- Configuration system
- Cross-platform support
December 2024: Feature expansion (0.1.1-0.1.4)
- Multi-site support
- SSL/TLS capabilities
- Performance optimizations
- Security enhancements
December 2024: Production readiness (0.1.5)
- WASM support
- Security hardening
- Comprehensive CI/CD
- Documentation project
Technology Evolution
Framework Choice:
- Selected Pingora for proven performance and security
- Rust for memory safety and performance
- TOML for human-readable configuration
Feature Development:
- Started with basic static serving
- Added multi-site capabilities
- Implemented security features
- Enhanced performance and monitoring
Quality Assurance:
- Implemented comprehensive testing
- Added automated CI/CD pipelines
- Security scanning and vulnerability management
- Documentation and user guides
Community Growth
Open Source:
- MIT license for maximum compatibility
- Public development on GitHub
- Community contributions welcome
- Transparent development process
Ecosystem:
- Docker container support
- Package manager distributions
- Integration guides and examples
- Third-party tool compatibility
Future Vision
Short-term Goals:
- Enhanced monitoring and observability
- Plugin system for extensibility
- Advanced caching mechanisms
- GUI configuration tools
Long-term Vision:
- Enterprise-grade web server platform
- Comprehensive hosting solution
- Cloud-native deployment options
- Ecosystem of extensions and tools
For the latest information, see GitHub Releases