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.70 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
Available Docker Tags
latest
- Latest stable releasev0.1.5
- 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
# Build in debug mode
cargo build
# Build optimized release
cargo build --release
# The binary will be in target/release/bws
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 up and running in just a few minutes with both HTTP and HTTPS sites!
1. Create Configuration
Create a config.toml
file with both HTTP and HTTPS sites:
[server]
name = "BWS Multi-Site Server"
# HTTP site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
[sites.ssl]
enabled = false
[sites.headers]
"X-Site-Name" = "BWS Main Site"
"X-Powered-By" = "BWS/1.0"
# HTTPS site with automatic SSL
[[sites]]
name = "secure"
hostname = "secure.localhost"
port = 8443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.localhost", "ssl.localhost"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = true # Use staging for testing
challenge_dir = "./acme-challenges"
[sites.headers]
"X-Site-Name" = "BWS Secure Site"
"X-Powered-By" = "BWS/1.0"
"Strict-Transport-Security" = "max-age=31536000"
2. Create Static Content
Create your static directory and add some content:
# Create directories
mkdir -p static acme-challenges
# Create a simple index.html
cat > static/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Welcome to BWS</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
h1 { color: #333; }
.secure { color: #28a745; }
</style>
</head>
<body>
<h1>๐ Welcome to BWS!</h1>
<p>Your multi-site web server is running!</p>
<h2>Available Sites:</h2>
<ul>
<li><a href="http://localhost:8080">Main Site (HTTP)</a></li>
<li><a href="https://secure.localhost:8443" class="secure">Secure Site (HTTPS)</a></li>
</ul>
<h2>API Endpoints:</h2>
<ul>
<li><a href="/api/health">Health Check</a></li>
<li><a href="/api/sites">Sites Info</a></li>
</ul>
</body>
</html>
EOF
3. Run the Server
Start BWS with your configuration:
# Using cargo install
bws --config config.toml
# Using Docker
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
# From source
cargo run -- --config config.toml
4. Test Your Server
Open your browser or use curl to test both HTTP and HTTPS sites:
# Main HTTP site
curl http://localhost:8080/
# Health check
curl http://localhost:8080/api/health
# Sites information
curl http://localhost:8080/api/sites
# Test HTTPS site (may need to add hostname to /etc/hosts)
curl -k https://secure.localhost:8443/
# Check SSL certificate (if ACME is working)
openssl s_client -connect localhost:8443 -servername secure.localhost
Setting up hostname resolution
For testing HTTPS sites with custom hostnames, add entries to /etc/hosts
:
# Add to /etc/hosts
echo "127.0.0.1 secure.localhost" | sudo tee -a /etc/hosts
echo "127.0.0.1 ssl.localhost" | sudo tee -a /etc/hosts
ACME Certificate Notes
- The example uses
staging = true
for Let's Encrypt staging environment - Staging certificates are not trusted by browsers but are good for testing
- For production, set
staging = false
after testing - ACME certificates require your domain to be publicly accessible
You should see:
- Your custom HTML page at
http://localhost:8080/
- Health status at
http://localhost:8080/api/health
- Site configuration at
http://localhost:8080/api/sites
- SSL-secured content at
https://secure.localhost:8443/
(if configured)
5. Add WebSocket Proxy (Optional)
BWS can also proxy WebSocket connections with load balancing. Here's a simple example:
# Add to your existing configuration
[[sites]]
name = "websocket-proxy"
hostname = "ws.localhost"
port = 8090
static_dir = "static"
[sites.proxy]
enabled = true
# WebSocket upstream servers
[[sites.proxy.upstreams]]
name = "websocket_backend"
url = "http://localhost:3001" # Will be converted to ws://localhost:3001
weight = 1
[[sites.proxy.upstreams]]
name = "websocket_backend"
url = "http://localhost:3002" # Will be converted to ws://localhost:3002
weight = 1
# WebSocket routes
[[sites.proxy.routes]]
path = "/ws"
upstream = "websocket_backend"
strip_prefix = true
websocket = true # Enable WebSocket proxying
# Load balancing for WebSocket connections
[sites.proxy.load_balancing]
method = "round_robin"
Test WebSocket proxying:
# Start simple WebSocket test servers (if you have Node.js)
npx ws ws://localhost:3001 &
npx ws ws://localhost:3002 &
# Connect through BWS proxy
# WebSocket connections to ws://ws.localhost:8090/ws will be load-balanced
7. Add More Sites (Optional)
Extend your configuration to host multiple sites with different SSL configurations:
[server]
name = "BWS Multi-Site Server"
# Main HTTP site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
[sites.ssl]
enabled = false
[sites.headers]
"X-Site-Name" = "BWS Main Site"
# Blog site with auto SSL
[[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"
staging = true
challenge_dir = "./acme-challenges"
[sites.headers]
"X-Site-Name" = "BWS Blog"
"X-Content-Type" = "blog-content"
"Strict-Transport-Security" = "max-age=31536000"
# API documentation with manual SSL
[[sites]]
name = "docs"
hostname = "docs.localhost"
port = 8444
static_dir = "static-docs"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/docs.localhost.crt"
key_file = "./certs/docs.localhost.key"
[sites.headers]
"X-Site-Name" = "BWS Documentation"
"Access-Control-Allow-Origin" = "*"
"Strict-Transport-Security" = "max-age=31536000"
Create the corresponding directories and certificates:
# Create directories
mkdir static-blog static-docs certs
# Add content
echo "<h1>Blog Site (HTTPS)</h1>" > static-blog/index.html
echo "<h1>Documentation Site (HTTPS)</h1>" > static-docs/index.html
# Generate self-signed certificate for docs site
openssl req -x509 -newkey rsa:4096 -keyout certs/docs.localhost.key -out certs/docs.localhost.crt -days 365 -nodes -subj "/CN=docs.localhost"
chmod 600 certs/docs.localhost.key
Restart BWS and access:
- Main site:
http://localhost:8080
- Blog:
https://blog.localhost:8443
(auto SSL) - Docs:
https://docs.localhost:8444
(manual SSL)
Command Line Options
BWS supports several command line options:
# Specify config file
bws --config /path/to/config.toml
# Enable verbose logging
bws --verbose
# Run as daemon (Unix only)
bws --daemon
# Custom PID and log files for daemon mode
bws --daemon \
--pid-file /var/run/bws.pid \
--log-file /var/log/bws.log
Next Steps
- Learn more about SSL/TLS Configuration for HTTPS setup
- Explore Configuration options
- Set up Multi-Site hosting for complex deployments
- Configure Custom Headers for security and functionality
- Deploy with Docker for production use
Configuration
BWS uses TOML configuration files to define server behavior and site settings.
Configuration File Lkey_file = "./certs/manual.crt"
### Reverse Proxy Configuration
Each site can be configured as a reverse proxy with load balancing:
| Field | Type | Description | Default |
|-------|------|-------------|---------|
| `proxy.enabled` | Boolean | Enable reverse proxy for this site | `false` |
| `proxy.upstreams` | Array | Backend servers for proxying | `[]` |
| `proxy.upstreams.name` | String | Upstream group name | Required |
| `proxy.upstreams.url` | String | Backend server URL | Required |
| `proxy.upstreams.weight` | Integer | Server weight for load balancing | `1` |
| `proxy.routes` | Array | Proxy route configurations | `[]` |
| `proxy.routes.path` | String | Path pattern to match | Required |
| `proxy.routes.upstream` | String | Upstream group to proxy to | Required |
| `proxy.routes.strip_prefix` | Boolean | Remove path prefix when forwarding | `false` |
| `proxy.routes.websocket` | Boolean | Enable WebSocket proxying for this route | `false` |
| `proxy.load_balancing.method` | String | Load balancing algorithm | `"round_robin"` |
| `proxy.timeout.read` | Integer | Read timeout in seconds | `30` |
| `proxy.timeout.write` | Integer | Write timeout in seconds | `30` |
| `proxy.headers.add_x_forwarded` | Boolean | Add X-Forwarded-* headers | `true` |
| `proxy.headers.add_forwarded` | Boolean | Add Forwarded header | `true` |
| `proxy.headers.add` | Table | Custom headers to add | `{}` |
| `proxy.headers.remove` | Table | Headers to remove | `{}` |
### Example Proxy Configuration
```toml
# Reverse proxy site with load balancing
[[sites]]
name = "api"
hostname = "api.example.com"
port = 80
[sites.proxy]
enabled = true
# Multiple backend servers (same name for load balancing)
[[sites.proxy.upstreams]]
name = "api-servers"
url = "http://127.0.0.1:3001"
weight = 2
[[sites.proxy.upstreams]]
name = "api-servers"
url = "http://127.0.0.1:3002"
weight = 1
# Route configuration
[[sites.proxy.routes]]
path = "/v1/"
upstream = "api-servers"
strip_prefix = false
# Load balancing method (round_robin, weighted, least_connections)
[sites.proxy.load_balancing]
method = "weighted"
# Timeout configuration
[sites.proxy.timeout]
read = 30
write = 30
# Header management
[sites.proxy.headers]
add_x_forwarded = true
add_forwarded = true
[sites.proxy.headers.add]
"X-Proxy-Version" = "BWS/1.0"
[sites.proxy.headers.remove]
"X-Internal-Token" = true
key_file = "./certs/manual.key"ation
By default, BWS looks for config.toml
in the current directory. You can specify a different location:
bws --config /path/to/your/config.toml
Basic Configuration Structure
[server]
name = "BWS Multi-Site Server"
[[sites]]
name = "example"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
[sites.headers]
"X-Custom-Header" = "value"
Server Section
The [server]
section contains global server settings:
Field | Type | Description | Default |
---|---|---|---|
name | String | Server identification name | "BWS Server" |
[server]
name = "My Production BWS Server"
Sites Configuration
Sites are defined using [[sites]]
array tables. Each site represents a separate web service with its own SSL configuration.
Required Fields
Field | Type | Description |
---|---|---|
name | String | Unique identifier for the site |
hostname | String | Hostname to bind to |
port | Integer | Port number to listen on |
static_dir | String | Directory containing static files |
Optional Fields
Field | Type | Description | Default |
---|---|---|---|
default | Boolean | Whether this is the default site | false |
api_only | Boolean | Only serve API endpoints, no static files | false |
SSL Configuration
Each site can have its own SSL/TLS configuration:
Field | Type | Description | Default |
---|---|---|---|
ssl.enabled | Boolean | Enable SSL for this site | false |
ssl.auto_cert | Boolean | Use automatic certificates (ACME) | false |
ssl.domains | Array | Additional domains for the certificate | [] |
ssl.cert_file | String | Path to certificate file (manual SSL) | null |
ssl.key_file | String | Path to private key file (manual SSL) | null |
ssl.acme.enabled | Boolean | Enable ACME certificate generation | false |
ssl.acme.email | String | Email for ACME registration | null |
ssl.acme.staging | Boolean | Use staging environment | false |
ssl.acme.challenge_dir | String | Directory for ACME challenges | "./acme-challenges" |
Example Site Configuration
# HTTP Site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
[sites.ssl]
enabled = false
# 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", "ssl.localhost"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "./acme-challenges"
# HTTPS Site with Manual SSL
[[sites]]
name = "manual_ssl"
hostname = "manual.localhost"
port = 8444
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/manual.crt"
key_file = "./certs/manual.key"
Complete Example
Here's a comprehensive configuration example with SSL:
[server]
name = "BWS Production Server"
# Main website with HTTP
[[sites]]
name = "main"
hostname = "example.com"
port = 80
static_dir = "static"
default = true
[sites.ssl]
enabled = false
[sites.headers]
"X-Site-Name" = "Main Website"
"X-Powered-By" = "BWS/1.0"
# Main website with HTTPS (automatic SSL)
[[sites]]
name = "main_https"
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
challenge_dir = "./acme-challenges"
[sites.headers]
"X-Site-Name" = "Main Website (HTTPS)"
"X-Powered-By" = "BWS/1.0"
"Strict-Transport-Security" = "max-age=31536000"
# Blog subdomain with manual SSL
[[sites]]
name = "blog"
hostname = "blog.example.com"
port = 443
static_dir = "blog-static"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "/etc/ssl/certs/blog.example.com.crt"
key_file = "/etc/ssl/private/blog.example.com.key"
[sites.headers]
"X-Site-Name" = "Blog"
"X-Content-Type" = "blog-content"
"Strict-Transport-Security" = "max-age=31536000"
Next Steps
- Learn about SSL/TLS Configuration for HTTPS setup
- Configure Multi-Site Setup for hosting multiple websites
- Set up Reverse Proxy for backend service integration
- Configure Load Balancing for high availability
- Set up Custom Headers for enhanced functionality
- Configure Health Monitoring for production use
Multi-Site Setup
BWS excels at hosting multiple websites on different ports with individual configurations. This guide shows you how to set up and manage multiple sites.
Basic Multi-Site Configuration
Here's a complete example with four different sites including SSL configurations:
[server]
name = "BWS Multi-Site Server"
# Main website (HTTP)
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true
[sites.ssl]
enabled = false
[sites.headers]
"X-Site-Name" = "Main Website"
"X-Powered-By" = "BWS/1.0"
"X-Environment" = "production"
# Blog subdomain (HTTPS with auto SSL)
[[sites]]
name = "blog"
hostname = "blog.localhost"
port = 8443
static_dir = "static-blog"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["blog.localhost", "www.blog.localhost"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "./acme-challenges"
[sites.headers]
"X-Site-Name" = "Blog"
"X-Content-Type" = "blog-content"
"X-Author" = "BWS Team"
"Strict-Transport-Security" = "max-age=31536000"
# API documentation (HTTPS with manual SSL)
[[sites]]
name = "docs"
hostname = "docs.localhost"
port = 8444
static_dir = "static-docs"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/docs.localhost.crt"
key_file = "./certs/docs.localhost.key"
[sites.headers]
"X-Site-Name" = "API Documentation"
"Access-Control-Allow-Origin" = "*"
"Access-Control-Allow-Methods" = "GET, OPTIONS"
"Strict-Transport-Security" = "max-age=31536000"
# Development environment (HTTP)
[[sites]]
name = "dev"
hostname = "localhost"
port = 8083
static_dir = "static-dev"
[sites.ssl]
enabled = false
[sites.headers]
"X-Site-Name" = "Development Environment"
"X-Environment" = "development"
"X-Debug-Mode" = "enabled"
Directory Structure
Create the corresponding directory structure including SSL certificate directories:
mkdir -p static static-blog static-docs static-dev acme-challenges certs
# Main site
cat > static/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Main Site</title></head>
<body>
<h1>๐ Main Website</h1>
<p>Welcome to the main BWS site!</p>
<nav>
<a href="https://blog.localhost:8443">Blog (HTTPS)</a> |
<a href="https://docs.localhost:8444">Docs (HTTPS)</a> |
<a href="http://localhost:8083">Dev (HTTP)</a>
</nav>
</body>
</html>
EOF
# Blog site
cat > static-blog/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>BWS Blog</title></head>
<body>
<h1>๐ BWS Blog (HTTPS)</h1>
<p>Latest news and updates - Secured with SSL</p>
<article>
<h2>Welcome to BWS Blog</h2>
<p>This is our blog powered by BWS multi-site hosting with automatic SSL.</p>
</article>
</body>
</html>
EOF
# Documentation site
cat > static-docs/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>BWS Documentation</title></head>
<body>
<h1>๐ BWS Documentation (HTTPS)</h1>
<p>Complete API and user documentation - Secured with manual SSL</p>
<ul>
<li><a href="/api/health">Health Check</a></li>
<li><a href="/api/sites">Sites Info</a></li>
</ul>
</body>
</html>
EOF
# Development site
cat > static-dev/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>BWS Development</title></head>
<body>
<h1>๐ง BWS Development</h1>
<p>Development and testing environment (HTTP only)</p>
<p><strong>Debug Mode:</strong> Enabled</p>
</body>
</html>
EOF
Site Types
Default Site
One site should be marked as default = true
. This site handles requests that don't match other hostnames:
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true # This is the default site
[sites.ssl]
enabled = false
HTTPS Sites
Sites can be configured with SSL/TLS for secure connections:
# Automatic SSL with ACME (Let's Encrypt)
[[sites]]
name = "secure_auto"
hostname = "secure.example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.example.com", "www.secure.example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "./acme-challenges"
# Manual SSL certificates
[[sites]]
name = "secure_manual"
hostname = "manual.example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "/etc/ssl/certs/manual.example.com.crt"
key_file = "/etc/ssl/private/manual.example.com.key"
API-Only Sites
For sites that only serve API endpoints without static files:
[[sites]]
name = "api"
hostname = "api.localhost"
port = 8084
static_dir = "api-static"
api_only = true # Only serve /api/* endpoints
[sites.ssl]
enabled = true
auto_cert = true
domains = ["api.localhost"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "./acme-challenges"
[sites.headers]
"Access-Control-Allow-Origin" = "*"
"Content-Type" = "application/json"
Hostname Patterns
BWS supports various hostname patterns:
# Localhost with different ports
[[sites]]
hostname = "localhost"
port = 8080
# Subdomain
[[sites]]
hostname = "blog.example.com"
port = 8081
# Different domain
[[sites]]
hostname = "api.myservice.com"
port = 8082
# IP address
[[sites]]
hostname = "192.168.1.100"
port = 8083
Load Balancing
You can run multiple BWS instances and use a load balancer:
# nginx.conf
upstream bws_main {
server localhost:8080;
server localhost:8090; # Second BWS instance
}
upstream bws_blog {
server localhost:8081;
server localhost:8091; # Second BWS instance
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://bws_main;
}
}
server {
listen 80;
server_name blog.example.com;
location / {
proxy_pass http://bws_blog;
}
}
Testing Multi-Site Setup
- Start BWS:
bws --config config.toml
- Test each site:
# HTTP Main site
curl http://localhost:8080/
# HTTPS Blog (requires host header or /etc/hosts entry)
curl -k -H "Host: blog.localhost" https://localhost:8443/
# HTTPS Docs (manual SSL)
curl -k -H "Host: docs.localhost" https://localhost:8444/
# HTTP Dev site
curl http://localhost:8083/
- Check SSL certificates:
# Check auto SSL certificate
openssl s_client -connect localhost:8443 -servername blog.localhost
# Check manual SSL certificate
openssl s_client -connect localhost:8444 -servername docs.localhost
- Check site information:
curl http://localhost:8080/api/sites
Best Practices
Port Management
- Use sequential ports for easy management
- Document port assignments
- Avoid common service ports (22, 80, 443, etc.)
Directory Organization
project/
โโโ config.toml
โโโ static/ # Main site
โโโ static-blog/ # Blog content
โโโ static-docs/ # Documentation
โโโ static-api/ # API documentation
โโโ shared-assets/ # Common files (CSS, JS, images)
Security Considerations
- Use different headers for different environments
- Implement CORS properly for API sites
- Consider IP restrictions for development sites
Monitoring
Each site can be monitored independently:
# Check health of each site
curl http://localhost:8080/api/health
curl http://localhost:8081/api/health
curl http://localhost:8082/api/health
Troubleshooting
Port Conflicts
# Check if port is in use
lsof -i :8080
# Kill process using port
kill $(lsof -t -i:8080)
Hostname Resolution
Add entries to /etc/hosts
for testing:
127.0.0.1 blog.localhost
127.0.0.1 docs.localhost
127.0.0.1 api.localhost
Then access sites via:
- http://localhost:8080 (HTTP main site)
- https://blog.localhost:8443 (HTTPS blog with auto SSL)
- https://docs.localhost:8444 (HTTPS docs with manual SSL)
SSL Certificate Setup
For manual SSL sites, generate test certificates:
# Create certificate directory
mkdir -p certs
# Generate self-signed certificate for docs.localhost
openssl req -x509 -newkey rsa:4096 -keyout certs/docs.localhost.key -out certs/docs.localhost.crt -days 365 -nodes -subj "/CN=docs.localhost"
# Set proper permissions
chmod 600 certs/docs.localhost.key
chmod 644 certs/docs.localhost.crt
Configuration Validation
BWS validates configuration on startup:
- Duplicate site names โ Error
- Port conflicts โ Error
- Missing directories โ Warning (created automatically)
- Invalid hostnames โ Error
Next Steps
- Configure SSL/TLS Configuration for HTTPS setup
- Set up Custom Headers for each site
- Configure Health Monitoring for production monitoring
- Learn about Static File Serving optimization
SSL/TLS Configuration
BWS provides robust, production-ready SSL/TLS support with automatic certificate management, comprehensive error handling, and thread-safe operations. Each site can have its own HTTPS configuration, enabling mixed HTTP/HTTPS deployments with different certificates per domain.
Overview
BWS supports two types of SSL/TLS configuration:
- Automatic SSL Certificates - Using ACME (Let's Encrypt) with automatic renewal monitoring
- Manual SSL Certificates - Using your own SSL certificate files
Production-Ready Features
- Automatic Certificate Renewal: Background monitoring service checks and renews certificates
- Thread-Safe Operations: All SSL operations are safe for concurrent access
- Comprehensive Error Handling: Graceful error propagation with detailed logging
- Zero-Downtime Renewal: Certificates renewed without service interruption
- Robust Monitoring: Certificate expiration tracking and proactive renewal
Automatic SSL Certificates (ACME)
Basic ACME Configuration
[[sites]]
name = "auto_ssl_site"
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
challenge_dir = "./acme-challenges"
ACME Configuration Options
Field | Type | Description | Default |
---|---|---|---|
enabled | Boolean | Enable ACME certificate generation | false |
email | String | Email address for ACME registration | Required |
staging | Boolean | Use Let's Encrypt staging environment | false |
challenge_dir | String | Directory for ACME HTTP-01 challenges | "./acme-challenges" |
ACME Challenge Handling
BWS automatically handles ACME HTTP-01 challenges with robust error handling:
- Challenge Directory: Configure
challenge_dir
where ACME challenges will be served - Automatic Routing: BWS automatically serves files from
/.well-known/acme-challenge/
- Directory Creation: The challenge directory is created automatically if it doesn't exist
- Error Handling: Comprehensive error reporting for challenge failures
# Create challenge directory
mkdir -p ./acme-challenges
# BWS will automatically serve challenges from:
# http://yourdomain.com/.well-known/acme-challenge/TOKEN
Automatic Certificate Renewal
BWS includes a production-ready certificate monitoring system:
#![allow(unused)] fn main() { // Background service automatically monitors certificates // - Checks certificate expiration every hour // - Renews certificates when needed (before 30 days expiration) // - Handles renewal failures gracefully with logging // - Thread-safe operations prevent race conditions }
Renewal Features:
- Automatic Monitoring: Background service checks certificates hourly
- Proactive Renewal: Renews certificates 30 days before expiration
- Error Recovery: Comprehensive error handling with detailed logging
- Zero Downtime: Renewals happen without service interruption
- Thread Safety: All operations are safe for concurrent access
Production vs Staging
Staging Environment (staging = true
):
- Use for testing and development
- Higher rate limits
- Certificates are not trusted by browsers
- Recommended for initial setup
Production Environment (staging = false
):
- Use for live websites
- Lower rate limits (5 certificates per domain per week)
- Certificates are trusted by browsers
- Use only after testing with staging
# Testing configuration
[sites.ssl.acme]
enabled = true
email = "test@example.com"
staging = true # Use staging environment
challenge_dir = "./acme-challenges"
# Production configuration
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false # Use production environment
challenge_dir = "./acme-challenges"
Manual SSL Certificates
Basic Manual SSL Configuration
[[sites]]
name = "manual_ssl_site"
hostname = "secure.example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "/etc/ssl/certs/secure.example.com.crt"
key_file = "/etc/ssl/private/secure.example.com.key"
Certificate File Requirements
Certificate File (cert_file
):
- Must contain the SSL certificate in PEM format
- Can include intermediate certificates (certificate chain)
- File must be readable by the BWS process
Private Key File (key_file
):
- Must contain the private key in PEM format
- Should be protected with appropriate file permissions (600)
- Must correspond to the certificate
Certificate Generation
You can generate certificates using various methods:
Self-Signed Certificates (Development)
# Generate private key
openssl genrsa -out server.key 2048
# Generate certificate
openssl req -new -x509 -key server.key -out server.crt -days 365 -subj "/CN=localhost"
# Use in configuration
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./server.crt"
key_file = "./server.key"
Commercial SSL Certificates
# Generate private key
openssl genrsa -out example.com.key 2048
# Generate certificate signing request
openssl req -new -key example.com.key -out example.com.csr
# Submit CSR to certificate authority
# Download certificate and intermediate certificates
# Combine certificate with intermediate certificates
cat example.com.crt intermediate.crt > example.com-chain.crt
# Use in configuration
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "/etc/ssl/certs/example.com-chain.crt"
key_file = "/etc/ssl/private/example.com.key"
Per-Site SSL Configuration
Configuration Structure
Each site has its own SSL configuration section:
[[sites]]
name = "site_name"
hostname = "example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true # Enable SSL for this site
auto_cert = true # Use automatic certificates
domains = ["example.com", "www.example.com"] # Additional domains
cert_file = "path/to/cert.pem" # Manual certificate file
key_file = "path/to/key.pem" # Manual private key file
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "./acme-challenges"
SSL Configuration Options
Field | Type | Description | Required |
---|---|---|---|
enabled | Boolean | Enable SSL for this site | Yes |
auto_cert | Boolean | Use ACME for automatic certificates | Yes |
domains | Array | Additional domains for the certificate | No |
cert_file | String | Path to certificate file (manual SSL) | If auto_cert = false |
key_file | String | Path to private key file (manual SSL) | If auto_cert = false |
Multi-Site SSL Examples
Mixed HTTP and HTTPS Sites
[server]
name = "Mixed Protocol Server"
# HTTP site on port 80
[[sites]]
name = "http_site"
hostname = "example.com"
port = 80
static_dir = "static"
[sites.ssl]
enabled = false
# HTTPS site on port 443 with auto SSL
[[sites]]
name = "https_site"
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
challenge_dir = "./acme-challenges"
# API site with manual SSL
[[sites]]
name = "api_site"
hostname = "api.example.com"
port = 8443
static_dir = "api-static"
[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"
Different SSL Configurations per Site
# Main website with Let's Encrypt
[[sites]]
name = "main"
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
challenge_dir = "./acme-challenges"
# Corporate subdomain with commercial certificate
[[sites]]
name = "corporate"
hostname = "corp.example.com"
port = 443
static_dir = "corp-static"
[sites.ssl]
enabled = true
auto_cert = false
cert_file = "/etc/ssl/certs/corp.example.com.crt"
key_file = "/etc/ssl/private/corp.example.com.key"
# Development site without SSL
[[sites]]
name = "dev"
hostname = "dev.example.com"
port = 8080
static_dir = "dev-static"
[sites.ssl]
enabled = false
Security Best Practices
File Permissions
Ensure proper file permissions for SSL files:
# Certificate files can be world-readable
chmod 644 /etc/ssl/certs/*.crt
# Private keys should be readable only by the owner
chmod 600 /etc/ssl/private/*.key
# Ensure BWS can read the files
chown bws:bws /etc/ssl/private/*.key
Security Headers
Use security headers with HTTPS sites:
[[sites]]
name = "secure_site"
hostname = "example.com"
port = 443
static_dir = "static"
[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com"]
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false
challenge_dir = "./acme-challenges"
[sites.headers]
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains; preload"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"Referrer-Policy" = "strict-origin-when-cross-origin"
"Content-Security-Policy" = "default-src 'self'; script-src 'self' 'unsafe-inline'"
Certificate Monitoring
Monitor certificate expiration:
# Check certificate expiration
openssl x509 -in /etc/ssl/certs/example.com.crt -noout -dates
# For ACME certificates, BWS handles renewal automatically
# Check ACME certificate status in logs
tail -f /var/log/bws.log | grep -i acme
Troubleshooting
Common Issues
ACME Challenge Failures
# Ensure challenge directory is accessible
[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = true # Use staging for debugging
challenge_dir = "./acme-challenges"
Check that:
- Domain resolves to your server
- Port 80 is accessible for HTTP-01 challenges
- Challenge directory exists and is writable
- No firewall blocking HTTP traffic
Certificate File Errors
# Verify certificate file format
openssl x509 -in cert.pem -text -noout
# Verify private key format
openssl rsa -in key.pem -check
# Check if certificate and key match
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
Port Binding Issues
# Check if port is already in use
sudo lsof -i :443
# Ensure BWS has permission to bind to privileged ports
sudo setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/bws
Debug Mode
Enable verbose logging for SSL debugging:
# Start BWS with verbose logging
RUST_LOG=debug bws --config config.toml --verbose
Validation
Test SSL configuration:
# Test HTTPS connectivity
curl -I https://example.com
# Check SSL certificate
openssl s_client -connect example.com:443 -servername example.com
# Test with specific protocol versions
curl --tlsv1.2 -I https://example.com
curl --tlsv1.3 -I https://example.com
Next Steps
- Configure Custom Headers for enhanced security
- Set up Health Monitoring for SSL sites
- Learn about Production Deployment with SSL
- Review Multi-Site Setup for complex configurations
Static File Serving
BWS provides efficient static file serving with automatic MIME type detection, caching headers, and subdirectory support.
How 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-Control
headers - 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 Configuration
BWS now supports comprehensive Caddy-style reverse proxy functionality on a per-site basis. Each site can be configured to proxy specific routes to upstream servers with flexible routing, load balancing, and head๐ Future Enhancements:
- Health check system with automatic failover
- Connection pooling optimization
- Circuit breaker pattern for failing upstreams
- โ WebSocket proxy support - Real-time bidirectional communication proxying
- Request body streaming for large payloads
- Detailed metrics and monitoringgement.
Configuration Structure
Basic Proxy Configuration
[sites.proxy]
enabled = true
# Multiple backend servers (same name for load balancing)
[[sites.proxy.upstreams]]
name = "backend-pool"
url = "http://127.0.0.1:3001"
weight = 1
[[sites.proxy.upstreams]]
name = "backend-pool"
url = "http://127.0.0.1:3002"
weight = 2
# Route configuration
[[sites.proxy.routes]]
path = "/api/"
upstream = "backend-pool"
strip_prefix = false
# Load balancing configuration
[sites.proxy.load_balancing]
method = "round_robin" # Options: round_robin, weighted, least_connections
# Timeout configuration
[sites.proxy.timeout]
read = 30
write = 30
# Header management
[sites.proxy.headers]
add_x_forwarded = true
add_forwarded = true
[sites.proxy.headers.add]
"X-Powered-By" = "BWS"
[sites.proxy.headers.remove]
"X-Internal-Header" = true
Load Balancing Algorithms
1. Round Robin (round_robin
)
Distributes requests evenly across all available backend servers.
[sites.proxy.load_balancing]
method = "round_robin"
- How it works: Cycles through servers in order
- Best for: Servers with similar performance characteristics
- Provides: Fair distribution when all servers have equal capacity
2. Weighted (weighted
)
Distributes requests based on assigned weights.
[sites.proxy.load_balancing]
method = "weighted"
# Configure weights per server
[[sites.proxy.upstreams]]
name = "backend-pool"
url = "http://127.0.0.1:3001"
weight = 3 # Gets 3x traffic
[[sites.proxy.upstreams]]
name = "backend-pool"
url = "http://127.0.0.1:3002"
weight = 1 # Gets 1x traffic
- How it works: Random selection weighted by server capacity
- Best for: Servers with different capacities or performance
- Provides: Performance-based load distribution
3. Least Connections (least_connections
)
Routes requests to the server with the fewest active connections.
[sites.proxy.load_balancing]
method = "least_connections"
- How it works: Tracks active connections per server using atomic counters
- Best for: Long-running requests or varying response times
- Provides: Optimal connection distribution
Features
Multiple Upstream Servers
Group multiple backend servers under the same upstream name for automatic load balancing.
Flexible Routing
- Path-based routing: Route requests based on URL paths
- Prefix stripping: Remove path prefixes before forwarding to upstream
- Pattern matching: Match specific paths and route accordingly
Thread-Safe Load Balancing
- Atomic Counters: Thread-safe request distribution
- Connection Tracking: Real-time monitoring of active connections
- Zero Locks: High-performance concurrent access
Header Management
- Standard Proxy Headers: X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host
- RFC 7239 Forwarded Header: Modern standard forwarded header support
- Custom Headers: Add or remove custom headers per site
- Header Preservation: Configure which headers to preserve or modify
Request/Response Handling
- Full HTTP Support: All HTTP methods (GET, POST, PUT, DELETE, etc.)
- Body Forwarding: Complete request and response body forwarding
- Status Code Preservation: Maintains original response status codes
- Error Handling: Graceful fallback with proper error responses
Timeout Configuration
- Read Timeout: Configurable upstream read timeouts
- Write Timeout: Configurable upstream write timeouts
- Per-Site Settings: Individual timeout settings per site
Example Configurations
Basic API Proxy
[[sites]]
name = "api.example.com"
hostname = "api.example.com"
port = 80
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "api-backend"
url = "http://127.0.0.1:3000"
[[sites.proxy.routes]]
path = "/v1/"
upstream = "api-backend"
Load Balanced Application
[[sites]]
name = "app.example.com"
hostname = "app.example.com"
port = 80
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "app-server-1"
url = "http://10.0.1.10:8080"
weight = 2
[[sites.proxy.upstreams]]
name = "app-server-2"
url = "http://10.0.1.11:8080"
weight = 1
[[sites.proxy.routes]]
path = "/"
upstream = "app-servers"
[sites.proxy.load_balancing]
method = "weighted"
[sites.proxy.health_check]
enabled = true
interval_seconds = 15
path = "/health"
Mixed Static and Proxy
[[sites]]
name = "example.com"
hostname = "example.com"
port = 80
static_dir = "/var/www/example.com"
[sites.proxy]
enabled = true
[[sites.proxy.upstreams]]
name = "api"
url = "http://127.0.0.1:3000"
[[sites.proxy.upstreams]]
name = "admin"
url = "http://127.0.0.1:4000"
# Proxy API requests
[[sites.proxy.routes]]
path = "/api/"
upstream = "api"
strip_prefix = true
# Proxy admin interface
[[sites.proxy.routes]]
path = "/admin/"
upstream = "admin"
strip_prefix = false
# Static files are served for all other paths
Route Priority
When a request comes in, BWS checks routes in this order:
- API routes (
/api/health
,/api/config
, etc.) - BWS internal APIs - Proxy routes - Configured reverse proxy routes (most specific path first)
- Static files - Files from the site's static directory
Implementation Status
โ Fully Implemented:
- โ Per-site proxy configuration - Complete TOML configuration parsing
- โ Route detection and matching - Intelligent path-based routing
- โ Full HTTP proxy implementation - Complete request/response forwarding using reqwest
- โ Three load balancing algorithms - Round-robin, weighted, and least-connections
- โ Connection tracking - Atomic counters for least-connections algorithm
- โ Path transformation - Prefix stripping and URL rewriting
- โ Header management - X-Forwarded-*, Forwarded, and custom headers
- โ Error handling - Graceful fallback with 502 Bad Gateway responses
- โ Response forwarding - Complete status code, header, and body forwarding
- โ Timeout configuration - Configurable read/write timeouts
- โ Thread safety - Concurrent request handling with atomic operations
- โ Mixed mode operation - Static files and proxy routes work together
๏ฟฝ Future Enhancements:
- Health check system with automatic failover
- Connection pooling optimization
- Circuit breaker pattern for failing upstreams
- WebSocket proxy support
- Request body streaming for large payloads
- Detailed metrics and monitoring
WebSocket Proxy Support
BWS supports proxying WebSocket connections to upstream servers with the same load balancing capabilities as HTTP requests.
WebSocket Configuration
To enable WebSocket proxying for a route, set the websocket
flag to true
:
[[sites.proxy.routes]]
path = "/ws"
upstream = "websocket_backend"
strip_prefix = true
websocket = true # Enable WebSocket proxying
WebSocket Features
- Automatic Detection: BWS automatically detects WebSocket upgrade requests
- Load Balancing: WebSocket connections are distributed using the same algorithms as HTTP requests
- Bidirectional Communication: Full support for real-time message forwarding
- Protocol Upgrade: Automatic handling of HTTP to WebSocket protocol upgrade
- Header Forwarding: Proper forwarding of WebSocket-specific headers
Example WebSocket Configuration
[[sites]]
name = "websocket-app"
hostname = "ws.example.com"
port = 8080
static_dir = "./static"
[sites.proxy]
enabled = true
# WebSocket upstream servers
[[sites.proxy.upstreams]]
name = "chat_servers"
url = "http://localhost:3001" # Will be converted to ws://localhost:3001
weight = 1
[[sites.proxy.upstreams]]
name = "chat_servers"
url = "http://localhost:3002" # Will be converted to ws://localhost:3002
weight = 1
# WebSocket routes
[[sites.proxy.routes]]
path = "/ws/chat"
upstream = "chat_servers"
strip_prefix = true
websocket = true
[[sites.proxy.routes]]
path = "/ws/notifications"
upstream = "chat_servers"
strip_prefix = false
websocket = true
# Load balancing for WebSocket connections
[sites.proxy.load_balancing]
method = "round_robin" # or "weighted", "least_connections"
WebSocket URL Transformation
BWS automatically converts HTTP upstream URLs to WebSocket URLs:
http://localhost:3001
โws://localhost:3001
https://localhost:3001
โwss://localhost:3001
Testing WebSocket Proxy
Use the included test script to verify WebSocket proxy functionality:
./tests/test_websocket_proxy.sh
This will:
- Start multiple WebSocket test servers
- Configure BWS with WebSocket proxy routes
- Provide a web interface for testing connections
- Demonstrate load balancing between upstream servers
Testing
The reverse proxy functionality is fully operational with comprehensive load balancing! Here's how to test it:
Quick Test
# 1. Start multiple backend servers
python3 -m http.server 3001 &
python3 -m http.server 3002 &
python3 -m http.server 3003 &
# 2. Start BWS with load balancing configuration
cargo run -- --config tests/test_load_balancing.toml
# 3. Test load balancing
curl http://localhost:8080/api/test # Round-robin distribution
curl http://localhost:8081/ # Weighted distribution
curl http://localhost:8082/ # Least connections distribution
Comprehensive Load Balancing Test
Use the included test script to verify all load balancing algorithms:
./tests/test_load_balance.sh
This script will:
- Start mock backend servers
- Test round-robin distribution
- Test weighted distribution (60%/40%)
- Test least connections balancing
- Verify proper request distribution
Manual Testing
# Test specific load balancing methods
curl -H "Host: roundrobin.example.com" http://localhost:8080/api/test
curl -H "Host: weighted.example.com" http://localhost:8081/
curl -H "Host: leastconn.example.com" http://localhost:8082/
# Test header forwarding
curl -v -H "Host: proxy.localhost" http://localhost:8080/api/data
# Look for X-Forwarded-For, X-Forwarded-Proto headers
# Test static file serving still works
curl http://localhost:8080/
# Returns: static HTML content from BWS
Verified Features:
- โ Load Balancing: All three algorithms (round-robin, weighted, least-connections) working
- โ Connection Tracking: Least-connections properly tracks active connections
- โ Route Detection: Correctly identifies proxy vs static routes
- โ Path Transformation: Prefix stripping and rewriting work as configured
- โ HTTP Proxying: Full request/response proxying with all HTTP methods
- โ Header Forwarding: Request and response headers properly forwarded
- โ Error Handling: Returns 502 Bad Gateway when upstream is unavailable
- โ Thread Safety: Concurrent requests handled safely with atomic operations
- โ Timeouts: Configurable request timeouts prevent hanging
- โ Mixed Mode: Static files and proxy routes work together seamlessly
Performance Characteristics
- Round-Robin: O(1) selection complexity
- Weighted: O(n) selection complexity where n = number of servers
- Least Connections: O(n) selection complexity with atomic counters
- Thread Safety: Lock-free operations using atomic primitives
- Memory Efficiency: Minimal overhead with efficient data structures
This reverse proxy implementation makes BWS a complete web server solution, capable of serving static content, providing APIs, and proxying requests to backend services with enterprise-grade load balancing - just like Caddy!
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:3001
https://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
AtomicUsize
for 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
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
Docker Deployment
BWS provides comprehensive Docker support for easy deployment and containerization.
Docker Image
Official Docker Image
# Pull the latest image
docker pull ghcr.io/yourusername/bws:latest
# Pull a specific version
docker pull ghcr.io/yourusername/bws:0.1.5
Building from Source
# Clone the repository
git clone https://github.com/yourusername/bws.git
cd bws
# Build the Docker image
docker build -t bws:local .
Basic Usage
Simple Container
# Run BWS with default configuration
docker run -p 8080:8080 ghcr.io/yourusername/bws:latest
With Custom Configuration
# Run with custom config
docker run -p 8080:8080 \
-v $(pwd)/config.toml:/app/config.toml \
-v $(pwd)/static:/app/static \
ghcr.io/yourusername/bws:latest
Background Execution
# Run as daemon
docker run -d \
--name bws-server \
-p 8080:8080 \
--restart unless-stopped \
-v $(pwd)/config.toml:/app/config.toml \
-v $(pwd)/static:/app/static \
ghcr.io/yourusername/bws:latest
Docker Compose
Basic Setup
# docker-compose.yml
version: '3.8'
services:
bws:
image: ghcr.io/yourusername/bws:latest
ports:
- "8080:8080"
volumes:
- ./config.toml:/app/config.toml
- ./static:/app/static
- ./logs:/app/logs
environment:
- RUST_LOG=info
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Multi-Site Setup
# docker-compose.yml
version: '3.8'
services:
bws-main:
image: ghcr.io/yourusername/bws:latest
container_name: bws-main
ports:
- "8080:8080"
volumes:
- ./sites/main/config.toml:/app/config.toml
- ./sites/main/static:/app/static
- ./logs/main:/app/logs
environment:
- RUST_LOG=info
- BWS_SITE_NAME=main
restart: unless-stopped
networks:
- bws-network
bws-api:
image: ghcr.io/yourusername/bws:latest
container_name: bws-api
ports:
- "8081:8080"
volumes:
- ./sites/api/config.toml:/app/config.toml
- ./sites/api/static:/app/static
- ./logs/api:/app/logs
environment:
- RUST_LOG=info
- BWS_SITE_NAME=api
restart: unless-stopped
networks:
- bws-network
networks:
bws-network:
driver: bridge
With Reverse Proxy
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- bws
networks:
- web-network
bws:
image: ghcr.io/yourusername/bws:latest
expose:
- "8080"
volumes:
- ./config.toml:/app/config.toml
- ./static:/app/static
- ./logs:/app/logs
environment:
- RUST_LOG=info
restart: unless-stopped
networks:
- web-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
networks:
web-network:
driver: bridge
Environment Variables
Configuration via Environment
# Override configuration with environment variables
docker run -p 8080:8080 \
-e BWS_CONFIG=/app/config.toml \
-e BWS_LOG_FILE=/app/logs/bws.log \
-e BWS_PID_FILE=/app/bws.pid \
-e RUST_LOG=debug \
ghcr.io/yourusername/bws:latest
Available Environment Variables
# Core settings
BWS_CONFIG=/app/config.toml # Configuration file path
BWS_LOG_FILE=/app/logs/bws.log # Log file path
BWS_PID_FILE=/app/bws.pid # PID file path
# Logging
RUST_LOG=info # Log level
RUST_BACKTRACE=1 # Enable backtraces
# Application
BWS_SITE_NAME=main # Site identifier
BWS_BIND_ADDRESS=0.0.0.0 # Bind address
BWS_PORT=8080 # Default port
Volume Mounting
Essential Volumes
# Configuration and static files
docker run -p 8080:8080 \
-v $(pwd)/config.toml:/app/config.toml:ro \
-v $(pwd)/static:/app/static:ro \
-v $(pwd)/logs:/app/logs \
ghcr.io/yourusername/bws:latest
Development Setup
# Mount source for development
docker run -p 8080:8080 \
-v $(pwd)/config.toml:/app/config.toml \
-v $(pwd)/static:/app/static \
-v $(pwd)/logs:/app/logs \
-v $(pwd)/data:/app/data \
ghcr.io/yourusername/bws:latest
Production Setup
# Production with named volumes
docker volume create bws-config
docker volume create bws-static
docker volume create bws-logs
docker run -p 8080:8080 \
-v bws-config:/app/config \
-v bws-static:/app/static \
-v bws-logs:/app/logs \
ghcr.io/yourusername/bws:latest
Health Checks
Docker Health Check
# Built into the image
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
Custom Health Check Script
#!/bin/bash
# health-check.sh
curl -f -s http://localhost:8080/health > /dev/null
if [ $? -eq 0 ]; then
exit 0
else
exit 1
fi
Docker Compose Health Check
services:
bws:
image: ghcr.io/yourusername/bws:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Networking
Bridge Network
# Create custom network
docker network create bws-network
# Run container on custom network
docker run --network bws-network \
--name bws-server \
-p 8080:8080 \
ghcr.io/yourusername/bws:latest
Host Network
# Use host networking (Linux only)
docker run --network host \
ghcr.io/yourusername/bws:latest
Internal Communication
# docker-compose.yml
services:
bws:
image: ghcr.io/yourusername/bws:latest
networks:
- internal
# No ports exposed externally
nginx:
image: nginx:alpine
ports:
- "80:80"
networks:
- internal
depends_on:
- bws
networks:
internal:
driver: bridge
internal: true # No external access
Security
Non-Root User
# Built into the image
USER bws
WORKDIR /app
Read-Only Root Filesystem
# Run with read-only root filesystem
docker run --read-only \
--tmpfs /tmp \
--tmpfs /app/logs \
-v $(pwd)/config.toml:/app/config.toml:ro \
-v $(pwd)/static:/app/static:ro \
-p 8080:8080 \
ghcr.io/yourusername/bws:latest
Security Options
# Enhanced security
docker run --security-opt=no-new-privileges \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
-p 8080:8080 \
ghcr.io/yourusername/bws:latest
Docker Compose Security
services:
bws:
image: ghcr.io/yourusername/bws:latest
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
tmpfs:
- /tmp
- /app/logs
Production Deployment
Resource Limits
# docker-compose.yml
services:
bws:
image: ghcr.io/yourusername/bws:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
Logging Configuration
services:
bws:
image: ghcr.io/yourusername/bws:latest
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels: "service=bws"
Monitoring Integration
services:
bws:
image: ghcr.io/yourusername/bws:latest
labels:
- "prometheus.io/scrape=true"
- "prometheus.io/port=8080"
- "prometheus.io/path=/metrics"
Docker Build Optimization
Multi-Stage Build
# Build stage
FROM rust:1.89-slim as builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release
# Runtime stage
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN useradd -r -s /bin/false bws
WORKDIR /app
COPY --from=builder /app/target/release/bws /usr/local/bin/
COPY --chown=bws:bws config.toml ./
COPY --chown=bws:bws static ./static
USER bws
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
CMD ["bws", "--config", "/app/config.toml"]
Build Arguments
ARG RUST_VERSION=1.89
ARG TARGET=x86_64-unknown-linux-gnu
FROM rust:${RUST_VERSION}-slim as builder
# ... build process ...
# Build with custom arguments
docker build --build-arg RUST_VERSION=1.89 -t bws:custom .
Troubleshooting
Container Won't Start
# Check container logs
docker logs bws-server
# Run interactively for debugging
docker run -it --entrypoint /bin/bash ghcr.io/yourusername/bws:latest
# Check configuration
docker run --rm -v $(pwd)/config.toml:/app/config.toml \
ghcr.io/yourusername/bws:latest --config-check
Port Binding Issues
# Check if port is already in use
netstat -tulpn | grep :8080
# Use different port
docker run -p 8081:8080 ghcr.io/yourusername/bws:latest
Volume Mount Problems
# Check file permissions
ls -la config.toml static/
# Fix permissions
chmod 644 config.toml
chmod -R 644 static/
Health Check Failures
# Test health endpoint manually
docker exec bws-server curl -f http://localhost:8080/health
# Check health check logs
docker inspect bws-server | jq '.[0].State.Health'
Best Practices
Image Management
- Use specific version tags, not
latest
- Regularly update base images
- Scan images for vulnerabilities
- Use multi-stage builds to reduce image size
Configuration
- Use external configuration files
- Store secrets securely (Docker secrets, environment variables)
- Mount configuration as read-only
- Validate configuration before deployment
Monitoring
- Always include health checks
- Monitor resource usage
- Set up log aggregation
- Use structured logging
Security
- Run as non-root user
- Use read-only filesystems when possible
- Drop unnecessary capabilities
- Regularly update dependencies
Next Steps
- Configure Production Environment
- Set up Performance Monitoring
- Learn about Troubleshooting
Daemon Mode Configuration
BWS can run as a system daemon (background service) for production deployments, providing automatic startup, monitoring, and management capabilities.
Daemon Overview
Running BWS as a daemon provides:
- Automatic startup on system boot
- Process monitoring and restart capabilities
- Centralized logging and management
- Integration with system monitoring tools
- Proper signal handling and graceful shutdown
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 responds to standard Unix signals:
# Graceful shutdown
sudo kill -TERM $(cat /var/run/bws.pid)
# Reload configuration
sudo kill -HUP $(cat /var/run/bws.pid)
# Force restart
sudo kill -USR1 $(cat /var/run/bws.pid)
# Force termination (last resort)
sudo kill -KILL $(cat /var/run/bws.pid)
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
This guide covers deploying BWS in production environments with security, performance, and reliability best practices.
Production Readiness
BWS has been significantly hardened for production use with comprehensive improvements:
Reliability Enhancements
- Zero Panic Policy: All
.unwrap()
calls replaced with proper error handling - Comprehensive Error Handling: Graceful error propagation throughout the codebase
- Thread-Safe Operations: Fixed all race conditions and concurrency issues
- Automatic Certificate Renewal: Background monitoring service for SSL certificates
- Memory Safety: Rust's ownership system prevents memory corruption and data races
Code Quality Assurance
- Lint-Free Codebase: Passes all Clippy warnings for maximum code quality
- Modern Rust Patterns: Updated to use latest async/await and error handling patterns
- Resource Management: Proper cleanup of connections and certificate operations
- Production-Grade Logging: Structured logging with comprehensive error documentation
Production Architecture
Recommended Infrastructure
Internet โ Load Balancer โ Reverse Proxy โ BWS Instances
โ
Monitoring & Logging
High Availability Setup
Load Balancer (HAProxy/Nginx)
/ \
BWS Instance 1 BWS Instance 2
| |
Static Files Static Files
(NFS/S3) (NFS/S3)
| |
Health Monitor Health Monitor
Security Configuration
SSL/TLS Termination
BWS is typically deployed behind a reverse proxy that handles SSL termination:
# /etc/nginx/sites-available/bws-production
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL Configuration
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Rate Limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=static:10m rate=50r/s;
location / {
limit_req zone=static burst=20 nodelay;
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;
# Timeouts
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
}
location /api/ {
limit_req zone=api burst=5 nodelay;
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;
}
location /health {
proxy_pass http://bws_backend/health;
access_log off;
allow 127.0.0.1;
allow 10.0.0.0/8;
deny all;
}
}
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 backup;
keepalive 32;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
Firewall Configuration
# UFW (Ubuntu Firewall) example
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 10.0.0.0/8 to any port 8080 # Internal BWS access
sudo ufw enable
# iptables example
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # SSH
iptables -A INPUT -p tcp --dport 80 -j ACCEPT # HTTP
iptables -A INPUT -p tcp --dport 443 -j ACCEPT # HTTPS
iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 8080 -j ACCEPT # BWS internal
iptables -A INPUT -j DROP
Production Configuration
Optimized BWS Configuration
# /etc/bws/production.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
format = "json"
[monitoring]
enabled = true
health_endpoint = "/health"
detailed_endpoint = "/health/detailed"
metrics_endpoint = "/metrics"
[monitoring.checks]
disk_threshold = 85
memory_threshold = 80
response_time_threshold = 1000
[performance]
max_connections = 10000
worker_threads = 8
keep_alive_timeout = 30
request_timeout = 30
max_request_size = "10MB"
# Production site
[[sites]]
name = "production"
hostname = "127.0.0.1" # Behind reverse proxy
port = 8080
static_dir = "/opt/bws/static"
[sites.headers]
"Cache-Control" = "public, max-age=31536000"
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "DENY"
"X-Served-By" = "BWS-Production"
"Vary" = "Accept-Encoding"
# API site
[[sites]]
name = "api"
hostname = "127.0.0.1"
port = 8081
static_dir = "/opt/bws/api-docs"
[sites.headers]
"Cache-Control" = "no-cache, no-store, must-revalidate"
"Content-Type" = "application/json"
"Access-Control-Allow-Origin" = "https://example.com"
"X-API-Version" = "v1.0.0"
Environment Variables
# /etc/environment.d/bws.conf
BWS_CONFIG=/etc/bws/production.toml
BWS_LOG_FILE=/var/log/bws/bws.log
BWS_PID_FILE=/var/run/bws.pid
RUST_LOG=info
RUST_BACKTRACE=0
BWS_ENV=production
Performance Optimization
System Tuning
# /etc/sysctl.d/99-bws.conf
# Network performance
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 3
# File descriptor limits
fs.file-max = 1048576
# Apply settings
sysctl -p /etc/sysctl.d/99-bws.conf
System Limits
# /etc/security/limits.d/bws.conf
bws soft nofile 65536
bws hard nofile 65536
bws soft nproc 4096
bws hard nproc 4096
File System Optimization
# Mount options for static files (add to /etc/fstab)
/dev/sdb1 /opt/bws/static ext4 defaults,noatime,nodiratime 0 0
# For high-performance scenarios, consider tmpfs for cache
tmpfs /opt/bws/cache tmpfs defaults,size=1G,mode=755,uid=bws,gid=bws 0 0
Monitoring and Alerting
Prometheus Configuration
# /etc/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "bws_rules.yml"
scrape_configs:
- job_name: 'bws'
static_configs:
- targets: ['localhost:8080', 'localhost:8081']
metrics_path: '/metrics'
scrape_interval: 5s
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
Alert Rules
# /etc/prometheus/bws_rules.yml
groups:
- name: bws.rules
rules:
- alert: BWS_Down
expr: up{job="bws"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "BWS instance is down"
description: "BWS instance {{ $labels.instance }} has been down for more than 1 minute."
- alert: BWS_HighResponseTime
expr: histogram_quantile(0.95, bws_response_time_seconds) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "BWS high response time"
description: "95th percentile response time is {{ $value }}s"
- alert: BWS_HighErrorRate
expr: rate(bws_requests_total{status=~"5.."}[5m]) > 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "BWS high error rate"
description: "Error rate is {{ $value }} errors per second"
- alert: BWS_HighMemoryUsage
expr: bws_memory_usage_bytes / bws_memory_limit_bytes > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "BWS high memory usage"
description: "Memory usage is {{ $value }}%"
Grafana Dashboard
{
"dashboard": {
"id": null,
"title": "BWS Production Dashboard",
"tags": ["bws", "production"],
"timezone": "browser",
"panels": [
{
"id": 1,
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(bws_requests_total[5m])",
"legendFormat": "{{ instance }}"
}
],
"yAxes": [
{
"label": "Requests/sec",
"min": 0
}
]
},
{
"id": 2,
"title": "Response Time",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.50, bws_response_time_seconds)",
"legendFormat": "50th percentile"
},
{
"expr": "histogram_quantile(0.95, bws_response_time_seconds)",
"legendFormat": "95th percentile"
}
]
},
{
"id": 3,
"title": "Error Rate",
"type": "singlestat",
"targets": [
{
"expr": "rate(bws_requests_total{status=~\"5..\"}[5m])",
"legendFormat": "5xx errors/sec"
}
]
}
]
}
}
Backup and Disaster Recovery
Configuration Backup
#!/bin/bash
# /usr/local/bin/backup-bws-config.sh
BACKUP_DIR="/backup/bws"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="bws-config-$DATE.tar.gz"
mkdir -p "$BACKUP_DIR"
# Backup configuration
tar -czf "$BACKUP_DIR/$BACKUP_FILE" \
/etc/bws/ \
/opt/bws/static/ \
/var/log/bws/ \
/etc/systemd/system/bws.service
# Keep only last 30 backups
find "$BACKUP_DIR" -name "bws-config-*.tar.gz" -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR/$BACKUP_FILE"
Log Backup and Rotation
# /etc/logrotate.d/bws
/var/log/bws/*.log {
daily
missingok
rotate 90
compress
delaycompress
notifempty
copytruncate
postrotate
systemctl reload bws
# Archive to S3 or backup system
aws s3 cp /var/log/bws/bws.log.1.gz s3://backup-bucket/logs/bws/$(date +%Y/%m/%d)/
endscript
}
Health Check and Recovery
#!/bin/bash
# /usr/local/bin/bws-recovery.sh
BWS_HEALTH_URL="http://localhost:8080/health"
LOG_FILE="/var/log/bws/recovery.log"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
send_alert() {
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"BWS Alert: $1\"}" \
"$SLACK_WEBHOOK"
}
# Check BWS health
if ! curl -f -s "$BWS_HEALTH_URL" > /dev/null; then
log_message "BWS health check failed, attempting recovery"
send_alert "BWS is down, attempting automatic recovery"
# Try graceful restart
systemctl restart bws
sleep 10
# Check if restart was successful
if curl -f -s "$BWS_HEALTH_URL" > /dev/null; then
log_message "BWS recovered successfully"
send_alert "BWS recovery successful"
else
log_message "BWS recovery failed"
send_alert "BWS recovery FAILED - manual intervention required"
exit 1
fi
else
log_message "BWS is healthy"
fi
Deployment Strategies
Blue-Green Deployment
#!/bin/bash
# /usr/local/bin/deploy-bws.sh
BLUE_PORT=8080
GREEN_PORT=8081
HEALTH_CHECK_URL="http://localhost"
deploy_to_port() {
local PORT=$1
local VERSION=$2
echo "Deploying BWS $VERSION to port $PORT"
# Stop existing service
systemctl stop bws-$PORT
# Update binary
cp /tmp/bws-$VERSION /usr/local/bin/bws-$PORT
# Update configuration
sed "s/port = .*/port = $PORT/" /etc/bws/config.toml > /etc/bws/config-$PORT.toml
# Start service
systemctl start bws-$PORT
# Health check
sleep 5
if curl -f -s "$HEALTH_CHECK_URL:$PORT/health" > /dev/null; then
echo "Deployment to port $PORT successful"
return 0
else
echo "Deployment to port $PORT failed"
return 1
fi
}
# Get current active port from load balancer
CURRENT_PORT=$(nginx -T 2>/dev/null | grep "server 127.0.0.1:" | head -1 | awk '{print $2}' | cut -d: -f2)
if [ "$CURRENT_PORT" = "$BLUE_PORT" ]; then
DEPLOY_PORT=$GREEN_PORT
SWITCH_FROM=$BLUE_PORT
else
DEPLOY_PORT=$BLUE_PORT
SWITCH_FROM=$GREEN_PORT
fi
echo "Deploying to $DEPLOY_PORT (current: $SWITCH_FROM)"
# Deploy to inactive port
if deploy_to_port $DEPLOY_PORT $1; then
# Switch load balancer
sed -i "s/server 127.0.0.1:$SWITCH_FROM/server 127.0.0.1:$DEPLOY_PORT/" /etc/nginx/sites-available/bws-production
nginx -s reload
echo "Switched load balancer to port $DEPLOY_PORT"
# Stop old instance after delay
sleep 30
systemctl stop bws-$SWITCH_FROM
echo "Deployment completed successfully"
else
echo "Deployment failed"
exit 1
fi
Rolling Updates
#!/bin/bash
# /usr/local/bin/rolling-update.sh
INSTANCES=("server1" "server2" "server3")
VERSION=$1
for instance in "${INSTANCES[@]}"; do
echo "Updating $instance..."
# Remove from load balancer
ssh $instance "nginx -s reload" # Remove from upstream
# Wait for connections to drain
sleep 30
# Deploy new version
ssh $instance "systemctl stop bws && cp /tmp/bws-$VERSION /usr/local/bin/bws && systemctl start bws"
# Health check
if ssh $instance "curl -f -s http://localhost:8080/health"; then
echo "$instance updated successfully"
# Add back to load balancer
ssh $instance "nginx -s reload" # Add to upstream
else
echo "Update failed on $instance"
exit 1
fi
sleep 10
done
echo "Rolling update completed"
Security Best Practices
Access Control
# Restrict access to BWS configuration
chmod 600 /etc/bws/config.toml
chown root:bws /etc/bws/config.toml
# Secure log files
chmod 640 /var/log/bws/*.log
chown bws:adm /var/log/bws/*.log
# Secure binary
chmod 755 /usr/local/bin/bws
chown root:root /usr/local/bin/bws
Network Security
# Disable unnecessary services
systemctl disable telnet
systemctl disable ftp
systemctl disable rsh
# Configure fail2ban for SSH protection
cat > /etc/fail2ban/jail.local << EOF
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
EOF
systemctl enable fail2ban
systemctl start fail2ban
Regular Security Updates
#!/bin/bash
# /usr/local/bin/security-updates.sh
# Run via cron: 0 2 * * 0 /usr/local/bin/security-updates.sh
# Update system packages
apt update && apt upgrade -y
# Update BWS if new version available
CURRENT_VERSION=$(bws --version | awk '{print $2}')
LATEST_VERSION=$(curl -s https://api.github.com/repos/yourusername/bws/releases/latest | jq -r .tag_name)
if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then
echo "BWS update available: $CURRENT_VERSION -> $LATEST_VERSION"
# Implement update process
fi
# Restart services if needed
if [ -f /var/run/reboot-required ]; then
echo "Reboot required after updates"
# Schedule maintenance window reboot
fi
Troubleshooting Production Issues
Common Issues and Solutions
High CPU Usage
# Monitor CPU usage
top -p $(pgrep bws)
htop -p $(pgrep bws)
# Check system load
uptime
iostat 1
# Review configuration
grep -E "worker_threads|max_connections" /etc/bws/config.toml
Memory Leaks
# Monitor memory usage over time
while true; do
ps -o pid,ppid,cmd,%mem,%cpu -p $(pgrep bws)
sleep 60
done > /tmp/bws-memory.log
# Check for memory leaks
valgrind --tool=memcheck --leak-check=full /usr/local/bin/bws
Network Issues
# Check port bindings
netstat -tulpn | grep bws
# Monitor connections
ss -tuln | grep :8080
lsof -i :8080
# Check network performance
iftop
nethogs
Disk Space Issues
# Check disk usage
df -h
du -sh /var/log/bws/
du -sh /opt/bws/
# Clean up logs
journalctl --vacuum-time=7d
logrotate -f /etc/logrotate.d/bws
Maintenance Procedures
Regular Maintenance Tasks
#!/bin/bash
# /usr/local/bin/bws-maintenance.sh
# Run weekly via cron
LOG_FILE="/var/log/bws/maintenance.log"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
log_message "Starting BWS maintenance"
# Check disk space
DISK_USAGE=$(df /opt/bws | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 85 ]; then
log_message "WARNING: Disk usage is ${DISK_USAGE}%"
fi
# Rotate logs
logrotate -f /etc/logrotate.d/bws
# Check configuration
if bws --config-check /etc/bws/config.toml; then
log_message "Configuration is valid"
else
log_message "ERROR: Configuration validation failed"
fi
# Update file permissions
chown -R bws:bws /opt/bws/static/
chmod -R 644 /opt/bws/static/*
# Clean temporary files
find /tmp -name "bws-*" -mtime +7 -delete
# Backup configuration
/usr/local/bin/backup-bws-config.sh
log_message "BWS maintenance completed"
Update Procedures
#!/bin/bash
# /usr/local/bin/update-bws.sh
NEW_VERSION=$1
if [ -z "$NEW_VERSION" ]; then
echo "Usage: $0 <version>"
exit 1
fi
echo "Updating BWS to version $NEW_VERSION"
# Backup current version
cp /usr/local/bin/bws /usr/local/bin/bws.backup
# Download new version
wget "https://github.com/yourusername/bws/releases/download/$NEW_VERSION/bws-linux-amd64" -O /tmp/bws-$NEW_VERSION
# Verify checksum (if available)
# wget "https://github.com/yourusername/bws/releases/download/$NEW_VERSION/checksums.txt" -O /tmp/checksums.txt
# sha256sum -c /tmp/checksums.txt
# Test new version
chmod +x /tmp/bws-$NEW_VERSION
if /tmp/bws-$NEW_VERSION --version; then
echo "New version validated"
else
echo "New version validation failed"
exit 1
fi
# Deploy using blue-green strategy
/usr/local/bin/deploy-bws.sh $NEW_VERSION
echo "BWS updated to version $NEW_VERSION"
Best Practices Summary
Configuration
- Use environment-specific configuration files
- Store sensitive data in environment variables or secret management systems
- Regularly validate configuration syntax
- Version control all configuration changes
Security
- Run BWS behind a reverse proxy with SSL termination
- Implement proper firewall rules
- Regular security updates and patches
- Monitor for security vulnerabilities
- Use non-root user for BWS process
Performance
- Tune system parameters for high-performance workloads
- Monitor resource usage continuously
- Implement proper caching strategies
- Use CDN for static assets when possible
Reliability
- Implement comprehensive health checks
- Set up automated monitoring and alerting
- Use deployment strategies that minimize downtime
- Regular backups of configuration and data
- Document incident response procedures
Monitoring
- Monitor both technical and business metrics
- Set up alerting with appropriate thresholds
- Regular review of logs and metrics
- Performance trend analysis
- Capacity planning based on growth projections
Next Steps
- Learn about Performance Tuning for optimization
- Review Configuration Schema for advanced options
- Check Troubleshooting for common issues
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 REST API for monitoring and management.
Endpoints
Health Check
GET /api/health
Returns the server health status.
Response:
{
"status": "healthy",
"timestamp": "2025-08-23T12:00:00Z",
"version": "0.1.5",
"uptime_seconds": 3600
}
Example:
curl http://localhost:8080/api/health
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
Response Headers
All API responses include these headers:
Content-Type: application/json
X-Powered-By: BWS/1.0
- Site-specific custom headers (if configured)
Error Responses
API endpoints return standard HTTP status codes:
200 OK
- Successful request404 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"]
}
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
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:info
output
(string, optional): Where to send logs. Values:stdout
,stderr
,file
. Default:stdout
format
(string, optional): Log format. Values:text
,json
. Default:text
file_path
(string, required if output="file"): Path to log filemax_size
(string, optional): Maximum size before rotation. Examples:10MB
,1GB
. Default:100MB
max_files
(integer, optional): Number of rotated files to keep. Default:10
compress
(boolean, optional): Compress rotated log files. Default:true
include_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:512
max_connections
(integer, optional): Maximum concurrent connections. Default:10000
keep_alive_timeout
(integer, optional): HTTP keep-alive timeout in seconds. Default:60
request_timeout
(integer, optional): Request processing timeout in seconds. Default:30
response_timeout
(integer, optional): Response sending timeout in seconds. Default:30
read_buffer_size
(string, optional): Buffer size for reading requests. Default:8KB
write_buffer_size
(string, optional): Buffer size for writing responses. Default:8KB
max_request_size
(string, optional): Maximum allowed request size. Default:1MB
connection_pool_size
(integer, optional): Size of connection pool. Default:100
connection_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:true
health_endpoint
(string, optional): Path for basic health checks. Default:/health
detailed_endpoint
(string, optional): Path for detailed health info. Default:/health/detailed
metrics_endpoint
(string, optional): Path for Prometheus metrics. Default:/metrics
Monitoring Checks:
disk_threshold
(integer, optional): Alert when disk usage exceeds this percentage. Default:90
memory_threshold
(integer, optional): Alert when memory usage exceeds this percentage. Default:80
response_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:false
endpoint
(string, optional): Metrics endpoint path. Default:/metrics
port
(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:false
max_memory
(string, optional): Maximum memory to use for cache. Default:100MB
ttl_default
(integer, optional): Default cache TTL in seconds. Default:3600
ttl_static
(integer, optional): TTL for static files in seconds. Default:86400
max_file_size
(string, optional): Maximum size of files to cache. Default:1MB
cache_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:false
level
(integer, optional): Compression level (1-9, higher = better compression). Default:6
min_size
(integer, optional): Minimum response size to compress in bytes. Default:1024
algorithms
(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:false
follow_symlinks
(boolean, optional): Follow symbolic links when serving files. Default:false
case_sensitive
(boolean, optional): Case-sensitive URL matching. Default:true
max_age
(integer, optional): Default Cache-Control max-age in seconds. Default:3600
cors_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:false
auto_cert
(boolean, optional): Use automatic certificate generation via ACME. Default:false
domains
(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:false
email
(string, required if enabled): Email address for ACME registrationstaging
(boolean, optional): Use Let's Encrypt staging environment for testing. Default:false
challenge_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:false
requests_per_minute
(integer, optional): Maximum requests per minute per IP. Default:60
burst_size
(integer, optional): Allow burst of requests above the rate. Default:10
block_duration
(integer, optional): How long to block an IP after rate limit exceeded. Default:300
whitelist
(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:false
auth_type
(string, optional): Authentication method. Values:basic
,digest
. Default:basic
auth_realm
(string, optional): Realm name for HTTP authentication. Default:BWS
auth_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
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 bench
for performance testing - Profile with
cargo flamegraph
when 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 check
during development instead ofcargo build
- Enable incremental compilation
- Use
sccache
for shared build cache - Consider using
mold
linker 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
- Integration Tests: Test component interactions
- End-to-End Tests: Test complete workflows
- Performance Tests: Measure performance characteristics
- Security Tests: Validate security measures
Running Tests
Basic Test Commands
# Run all 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
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); } }
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/
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.
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_SERVICE
capability - 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.
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.html
http://example.com/about/
โ/var/www/html/about/index.html
http://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:
.wasm
files 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 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.
[Unreleased]
Added
- ๐ก๏ธ 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/file
route 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