Keyboard shortcuts

Press โ† or โ†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

BWS is a high-performance, multi-site web server built with Pingora, Cloudflare's battle-tested proxy framework.

What is BWS?

BWS hosts multiple websites on different ports with individual configurations. It combines Pingora's reliability and performance with comprehensive error handling, automatic SSL management, and production-grade features.

Key Features

  • Multi-Site Support: Host multiple websites with individual configurations
  • Reverse Proxy & Load Balancing: Full proxy functionality with multiple algorithms
  • SSL/TLS Support: Automatic and manual certificate management
  • High Performance: Built on Pingora for enterprise-grade reliability
  • Security Focused: Zero-panic policy with comprehensive error handling
  • Container Ready: Docker support with multi-architecture images
  • Hot Reload: Zero-downtime configuration updates

Use Cases

  • Development environments: Quick multi-site testing
  • Static site hosting: Efficient multi-website serving
  • Reverse proxy: Load balancing to backend services
  • Microservices: Service routing and load balancing
  • API gateways: Centralized API entry point

Architecture

Built on Cloudflare's Pingora framework:

  • Battle-tested reliability (handles millions of requests)
  • High performance with low latency
  • Memory safety with Rust
  • Modern networking (HTTP/2, HTTP/3 ready)

Getting Started

Start with the Installation guide or jump to Quick Start for immediate setup.

Installation

BWS can be installed in several ways. Choose the method that best fits your needs.

The easiest way to install BWS is using Cargo:

cargo install bws-web-server

This will install the latest stable version from crates.io. The package name is bws-web-server, but the installed binary is named bws.

Prerequisites

  • Rust: Version 1.89.0 or later
  • Cargo: Comes with Rust installation

Docker is the recommended deployment method for production environments:

# Pull and run the latest version
docker run -d -p 8080:8080 ghcr.io/benliao/bws:latest

# Or with custom configuration
docker run -d \
  -p 8080:8080 \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  -v $(pwd)/static:/app/static:ro \
  ghcr.io/benliao/bws:latest

# Validate configuration before running
docker run --rm \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  ghcr.io/benliao/bws:latest \
  bws --config config.toml --dry-run

Available Docker Tags

  • latest - Latest stable release
  • v0.3.4 - Specific version (recommended for production)
  • main - Latest development build

From Source

For development or custom builds:

# Clone the repository
git clone https://github.com/benliao/bws.git
cd bws

# Validate the build configuration
cargo check

# Build in debug mode
cargo build --bin bws

# Build optimized release
cargo build --release --bin bws

# Test the binary
./target/release/bws --version
./target/release/bws --help

# Validate example configurations
./target/release/bws --config examples/basic-single-site.toml --dry-run

# The binary will be in target/release/bws

Testing Your Build

After building from source, run the test suite:

# Run unit tests
cargo test

# Run configuration validation tests
./tests/scripts/validate-configs.sh

# Run integration tests (if server dependencies available)
./tests/scripts/test_headers.sh

Pre-built Binaries

Download pre-built binaries from GitHub Releases:

Linux (x86_64)

wget https://github.com/benliao/bws/releases/latest/download/bws-linux-x86_64.tar.gz
tar -xzf bws-linux-x86_64.tar.gz
chmod +x bws

macOS (x86_64)

wget https://github.com/benliao/bws/releases/latest/download/bws-macos-x86_64.tar.gz
tar -xzf bws-macos-x86_64.tar.gz
chmod +x bws

macOS (ARM64/Apple Silicon)

wget https://github.com/benliao/bws/releases/latest/download/bws-macos-aarch64.tar.gz
tar -xzf bws-macos-aarch64.tar.gz
chmod +x bws

Windows (x86_64)

wget https://github.com/benliao/bws/releases/latest/download/bws-windows-x86_64.exe
# Note: Windows executable is directly usable

Verification

After installation, verify BWS is working:

# Check version
bws --version

# Display help
bws --help

Next Steps

Once installed, proceed to the Quick Start guide to set up your first BWS server!

Quick Start

Get BWS running in minutes with these simple options.

Option 1: Instant Directory Server

Serve files immediately without configuration:

# Serve current directory on port 80
bws .

# Serve specific directory on custom port  
bws /path/to/website --port 8080

# Validate setup before starting
bws /path/to/website --port 8080 --dry-run

Example:

mkdir my-website
echo "<h1>Welcome to BWS!</h1>" > my-website/index.html
bws my-website --port 8080

Option 2: Configuration File Setup

1. Create Configuration

Create config.toml:

[server]
name = "BWS Server"

# HTTP site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true

[sites.headers]
"X-Powered-By" = "BWS"

# HTTPS site with auto SSL
[[sites]]
name = "secure"
hostname = "secure.localhost"
port = 8443
static_dir = "static"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.localhost"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = true
challenge_dir = "./acme-challenges"

2. Create Content

mkdir -p static acme-challenges
cat > static/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>BWS Server</title></head>
<body>
    <h1>๐Ÿš€ BWS is Running!</h1>
    <ul>
        <li><a href="http://localhost:8080">HTTP Site</a></li>
        <li><a href="https://secure.localhost:8443">HTTPS Site</a></li>
        <li><a href="/api/health">Health Check</a></li>
    </ul>
</body>
</html>
EOF

3. Start Server

# Validate first
bws --config config.toml --dry-run

# Start server
bws --config config.toml

Test Your Setup

# Test HTTP
curl http://localhost:8080/

# Health check
curl http://localhost:8080/api/health

# Test HTTPS (add to /etc/hosts if needed)
curl -k https://secure.localhost:8443/

Management API

BWS includes an optional secure management API for configuration reloads. To enable it, add to your config:

[management]
enabled = true
host = "127.0.0.1"
port = 7654
api_key = "your-secure-key"  # Optional but recommended

Usage:

# Hot reload configuration
curl -X POST http://127.0.0.1:7654/api/config/reload

Command Options

# Common options
bws --config config.toml          # Use config file
bws --verbose                     # Enable verbose logging
bws --daemon                      # Run as daemon (Unix only)
bws --dry-run                     # Validate config only

Next Steps

Configuration

BWS uses TOML configuration files to define server and site settings.

Basic Structure

[server]
name = "BWS Server"

[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true

Configuration Validation

Always validate your configuration before starting:

bws --config config.toml --dry-run

Server Configuration

Global server settings:

[server]
name = "BWS Production Server"         # Server name

Sites Configuration

Define websites using [[sites]] arrays:

Required Fields

[[sites]]
name = "main"                          # Unique site identifier
hostname = "example.com"               # Domain name
port = 443                             # Port number
static_dir = "static"                  # Static files directory

Optional Fields

default = true                         # Default site (catches all requests)
api_only = false                       # API-only mode (no static files)
hostnames = ["www.example.com"]        # Additional hostnames

SSL/TLS Configuration

Automatic SSL (Let's Encrypt)

[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false                        # Use false for production
challenge_dir = "./acme-challenges"

Manual SSL

[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/example.com.crt"
key_file = "./certs/example.com.key"

Custom Headers

[sites.headers]
"X-Powered-By" = "BWS"
"Cache-Control" = "public, max-age=3600"
"Strict-Transport-Security" = "max-age=31536000"

Reverse Proxy

[sites.proxy]
enabled = true

[[sites.proxy.upstreams]]
name = "backend"
url = "http://127.0.0.1:3001"
weight = 1

[[sites.proxy.routes]]
path = "/api/"
upstream = "backend"
strip_prefix = false
websocket = false

[sites.proxy.load_balancing]
method = "round_robin"                 # round_robin, weighted, least_connections

Management API

[management]
enabled = true                         # Disabled by default - must enable explicitly
host = "127.0.0.1"                     # Always localhost for security
port = 7654
api_key = "your-secure-key"            # Optional but recommended

Usage:

# Reload configuration
curl -X POST http://127.0.0.1:7654/api/config/reload \
  -H "X-API-Key: your-secure-key"

Performance Settings

[performance]
worker_threads = 8
max_connections = 1000
keep_alive_timeout = 60
request_timeout = 30

Security Settings

[security]
hide_server_header = true
max_request_size = "10MB"

[security.security_headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"

Logging

[logging]
level = "info"                         # debug, info, warn, error
format = "combined"                    # combined, compact, json
log_requests = true

Complete Example

[server]
name = "BWS Production Server"

# Main HTTPS site
[[sites]]
name = "main"
hostname = "example.com"
port = 443
static_dir = "static"
default = true

[sites.headers]
"Strict-Transport-Security" = "max-age=31536000"
"X-Frame-Options" = "DENY"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false

# API proxy site
[[sites]]
name = "api"
hostname = "api.example.com"
port = 443
static_dir = "static"

[sites.proxy]
enabled = true

[[sites.proxy.upstreams]]
name = "api-backend"
url = "http://127.0.0.1:3001"

[[sites.proxy.routes]]
path = "/v1/"
upstream = "api-backend"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["api.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"

# Management API
[management]
enabled = true
port = 7654
api_key = "secure-random-key"

# Performance
[performance]
worker_threads = 8
max_connections = 1000

# Security
[security]
hide_server_header = true

[security.security_headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"

# Logging
[logging]
level = "info"
log_requests = true

File Locations

BWS looks for configuration files in this order:

  1. File specified with --config flag
  2. config.toml in current directory
  3. bws.toml in current directory

Environment Variables

Override configuration with environment variables:

  • BWS_CONFIG_FILE - Configuration file path
  • BWS_LOG_LEVEL - Logging level (debug, info, warn, error)
  • BWS_WORKERS - Number of worker threads

Multi-Site Setup

BWS hosts multiple websites with individual configurations on different ports or hostnames.

Basic Multi-Site Configuration

[server]
name = "BWS Multi-Site Server"

# Main HTTP site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"
default = true

[sites.headers]
"X-Site-Name" = "Main Website"

# HTTPS blog site
[[sites]]
name = "blog"
hostname = "blog.localhost"
port = 8443
static_dir = "static-blog"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["blog.localhost"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"

# API proxy site
[[sites]]
name = "api"
hostname = "api.localhost"
port = 8080
static_dir = "static"

[sites.proxy]
enabled = true

[[sites.proxy.upstreams]]
name = "backend"
url = "http://127.0.0.1:3001"

[[sites.proxy.routes]]
path = "/v1/"
upstream = "backend"

Setup Multi-Site Content

Create directories and content for each site:

# Create directories
mkdir -p static static-blog acme-challenges

# Main site content
cat > static/index.html << 'EOF'
<h1>Main Website</h1>
<p>Welcome to the main site</p>
<a href="https://blog.localhost:8443">Visit Blog</a>
EOF

# Blog content
cat > static-blog/index.html << 'EOF'
<h1>Blog Site</h1>
<p>This is the blog running on HTTPS</p>
<a href="http://localhost:8080">Back to Main</a>
EOF

Virtual Hosting (Same Port)

Host multiple sites on the same port using hostname routing:

[server]
name = "Virtual Hosting Server"

# Main site
[[sites]]
name = "main"
hostname = "www.example.com"
port = 80
static_dir = "sites/main"
default = true

# Blog site (same port, different hostname)
[[sites]]
name = "blog"
hostname = "blog.example.com"
port = 80
static_dir = "sites/blog"

# API site (same port, different hostname)
[[sites]]
name = "api"
hostname = "api.example.com"
port = 80
static_dir = "sites/api"

Testing Virtual Hosting

Add entries to /etc/hosts for local testing:

# Add to /etc/hosts
echo "127.0.0.1 www.example.com blog.example.com api.example.com" | sudo tee -a /etc/hosts

Test with curl:

curl -H "Host: www.example.com" http://localhost/
curl -H "Host: blog.example.com" http://localhost/
curl -H "Host: api.example.com" http://localhost/

Multi-Port Configuration

Run different services on different ports:

# HTTP on port 80
[[sites]]
name = "web"
hostname = "example.com"
port = 80
static_dir = "web"

# HTTPS on port 443
[[sites]]
name = "secure"
hostname = "example.com"
port = 443
static_dir = "web"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"

# API on port 8080
[[sites]]
name = "api"
hostname = "api.example.com"
port = 8080
static_dir = "api"

# Admin panel on port 9000
[[sites]]
name = "admin"
hostname = "admin.example.com"
port = 9000
static_dir = "admin"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["admin.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"

Site-Specific Configuration

Each site can have unique settings:

Headers

# Different headers per site
[[sites]]
name = "api"
hostname = "api.example.com"
port = 80
static_dir = "api"

[sites.headers]
"Access-Control-Allow-Origin" = "*"
"X-API-Version" = "v1"

[[sites]]
name = "blog"
hostname = "blog.example.com"
port = 80
static_dir = "blog"

[sites.headers]
"X-Content-Type" = "blog"
"Cache-Control" = "public, max-age=3600"

Security Settings

# Secure admin site
[[sites]]
name = "admin"
hostname = "admin.example.com"
port = 443
static_dir = "admin"

[sites.headers]
"Strict-Transport-Security" = "max-age=31536000"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["admin.example.com"]

Start Multi-Site Server

# Validate configuration
bws --config multi-site.toml --dry-run

# Start server
bws --config multi-site.toml

Testing Multiple Sites

# Test different sites
curl http://localhost:8080/                    # Main site
curl https://blog.localhost:8443/              # Blog site  
curl http://api.localhost:8080/v1/health       # API site

# Check site information
curl http://localhost:8080/api/sites

# Test virtual hosting
curl -H "Host: www.example.com" http://localhost/
curl -H "Host: blog.example.com" http://localhost/

Management

Hot Reload

Update configuration without stopping the server:

# Edit configuration file
nano multi-site.toml

# Reload configuration
curl -X POST http://127.0.0.1:7654/api/config/reload

Monitoring

# Check all sites health
curl http://localhost:8080/api/health

# Monitor logs for all sites
tail -f /var/log/bws.log | grep -E "(site:|error:|ssl:)"

Production Deployment

Directory Structure

/var/www/
โ”œโ”€โ”€ sites/
โ”‚   โ”œโ”€โ”€ main/
โ”‚   โ”œโ”€โ”€ blog/
โ”‚   โ”œโ”€โ”€ api/
โ”‚   โ””โ”€โ”€ admin/
โ”œโ”€โ”€ certs/
โ”œโ”€โ”€ acme-challenges/
โ””โ”€โ”€ config/
    โ””โ”€โ”€ multi-site.toml

Service Configuration

[server]
name = "Production Multi-Site"

[[sites]]
name = "main"
hostname = "example.com"
port = 443
static_dir = "/var/www/sites/main"
default = true

[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
challenge_dir = "/var/www/acme-challenges"

[[sites]]
name = "api"
hostname = "api.example.com"
port = 443
static_dir = "/var/www/sites/api"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["api.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
challenge_dir = "/var/www/acme-challenges"

Firewall Setup

# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Allow management API (localhost only)
sudo ufw allow from 127.0.0.1 to any port 7654

SSL/TLS Configuration

BWS supports both automatic (Let's Encrypt) and manual SSL certificates with per-site configuration.

Automatic SSL (Let's Encrypt)

Basic Setup

[[sites]]
name = "secure"
hostname = "example.com"
port = 443
static_dir = "static"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false                        # Use true for testing
challenge_dir = "./acme-challenges"

ACME Options

FieldDescriptionDefault
emailContact email for Let's EncryptRequired
stagingUse staging environment for testingfalse
challenge_dirDirectory for HTTP-01 challenges"./acme-challenges"

Challenge Setup

# Create challenge directory
mkdir -p ./acme-challenges

# BWS automatically serves challenges at:
# http://yourdomain.com/.well-known/acme-challenge/

Requirements:

  • Domain must be publicly accessible on port 80
  • DNS must point to your server
  • Challenge directory must be readable

Manual SSL Certificates

Configuration

[[sites]]
name = "manual-ssl"
hostname = "secure.local"
port = 8443
static_dir = "static"

[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/secure.local.crt"
key_file = "./certs/secure.local.key"

Generate Self-Signed Certificate

# Create certificates directory
mkdir -p certs

# Generate self-signed certificate
openssl req -x509 -newkey rsa:4096 \
  -keyout certs/secure.local.key \
  -out certs/secure.local.crt \
  -days 365 -nodes \
  -subj "/CN=secure.local"

# Set proper permissions
chmod 600 certs/secure.local.key
chmod 644 certs/secure.local.crt

Use Existing Certificates

# Copy your existing certificates
cp /path/to/your.crt certs/
cp /path/to/your.key certs/

# Update permissions
chmod 600 certs/your.key
chmod 644 certs/your.crt

Mixed HTTP/HTTPS Setup

Host different sites with different SSL configurations:

# HTTP site
[[sites]]
name = "http"
hostname = "example.com"
port = 80
static_dir = "static"

# HTTPS site with auto SSL
[[sites]]
name = "https-auto"
hostname = "secure.example.com"
port = 443
static_dir = "static"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"

# HTTPS site with manual SSL
[[sites]]
name = "https-manual"
hostname = "internal.example.com"
port = 8443
static_dir = "static"

[sites.ssl]
enabled = true
auto_cert = false
cert_file = "./certs/internal.crt"
key_file = "./certs/internal.key"

SSL Security Headers

Add security headers for HTTPS sites:

[sites.headers]
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "DENY"

Certificate Renewal

Automatic Renewal

  • Certificates are automatically renewed before expiration
  • No manual intervention required
  • Renewal status logged to server logs

Manual Renewal

# Check certificate expiration
openssl x509 -in certs/your.crt -text -noout | grep "Not After"

# Replace expired certificates
cp /path/to/new.crt certs/
cp /path/to/new.key certs/

# Reload BWS configuration
curl -X POST http://127.0.0.1:7654/api/config/reload

Testing SSL Configuration

Verify Certificate

# Check certificate details
openssl x509 -in certs/your.crt -text -noout

# Test SSL connection
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com

# Check certificate chain
curl -I https://yourdomain.com/

SSL Labs Test

Use SSL Labs to test your SSL configuration for security issues.

Troubleshooting

Common Issues

ACME Challenge Fails:

  • Verify domain points to your server
  • Check port 80 is accessible
  • Ensure challenge directory exists and is readable

Certificate Not Loading:

  • Check file paths in configuration
  • Verify file permissions (key file should be 600)
  • Check BWS logs for error messages

Mixed Content Warnings:

  • Ensure all resources use HTTPS URLs
  • Add security headers to prevent mixed content

Debug Commands

# Validate SSL configuration
bws --config config.toml --dry-run

# Check file permissions
ls -la certs/

# Test certificate
curl -v https://yourdomain.com/

# Check server logs
tail -f /var/log/bws.log

Static File Serving

BWS provides efficient static file serving with automatic MIME type detection, caching headers, and subdirectory support. BWS offers two ways to serve static files: instant directory serving and configuration-based serving.

Instant Directory Serving

The fastest way to serve static files is using BWS's built-in directory serving mode:

# Serve current directory
bws .

# Serve specific directory on custom port
bws /path/to/website --port 8080

# Windows example  
bws.exe C:\websites\mysite --port 8080

Features

  • No Configuration Required: Just point to a directory
  • Automatic Site Setup: Creates temporary configuration with sensible defaults
  • Cross-Platform: Handles Windows and Unix paths correctly
  • Default Index Files: Automatically serves index.html, index.htm, or default.html
  • Clean Paths: User-friendly path display on all platforms

Example

# Create test directory
mkdir my-site
echo "<h1>Hello BWS!</h1>" > my-site/index.html

# Start serving
bws my-site --port 8080

Output:

๐Ÿš€ Creating temporary web server:
   ๐Ÿ“ Directory: /path/to/my-site
   ๐ŸŒ Port: 8080
   ๐Ÿ”— URL: http://localhost:8080

๐ŸŒ Temporary web server ready!
๐Ÿ“ BWS Temporary Directory Server

Configuration-Based Static Serving

For production deployments and advanced features, use configuration files.

How Configuration-Based Static File Serving Works

BWS serves files from the static_dir configured for each site:

[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "static"  # Files served from this directory

Supported File Types

BWS automatically detects MIME types for common file formats:

Web Files

  • HTML: .html, .htm โ†’ text/html
  • CSS: .css โ†’ text/css
  • JavaScript: .js, .mjs โ†’ application/javascript
  • JSON: .json โ†’ application/json

Images

  • PNG: .png โ†’ image/png
  • JPEG: .jpg, .jpeg โ†’ image/jpeg
  • GIF: .gif โ†’ image/gif
  • SVG: .svg โ†’ image/svg+xml
  • WebP: .webp โ†’ image/webp
  • AVIF: .avif โ†’ image/avif
  • ICO: .ico โ†’ image/x-icon

Fonts

  • WOFF: .woff, .woff2 โ†’ font/woff
  • TTF: .ttf โ†’ font/ttf
  • OTF: .otf โ†’ font/otf
  • EOT: .eot โ†’ application/vnd.ms-fontobject

Media

  • MP4: .mp4 โ†’ video/mp4
  • WebM: .webm โ†’ video/webm
  • MP3: .mp3 โ†’ audio/mpeg
  • WAV: .wav โ†’ audio/wav
  • OGG: .ogg โ†’ audio/ogg

Documents

  • PDF: .pdf โ†’ application/pdf
  • XML: .xml โ†’ application/xml
  • Text: .txt โ†’ text/plain
  • Markdown: .md โ†’ text/markdown

Archives

  • ZIP: .zip โ†’ application/zip
  • Gzip: .gz โ†’ application/gzip
  • Tar: .tar โ†’ application/x-tar

Configuration

  • TOML: .toml โ†’ application/toml
  • YAML: .yaml, .yml โ†’ application/x-yaml

WebAssembly

  • WASM: .wasm โ†’ application/wasm

URL Patterns

BWS handles several URL patterns for static files:

Direct File Access

http://localhost:8080/index.html
http://localhost:8080/styles.css
http://localhost:8080/script.js

Static Directory Prefix

http://localhost:8080/static/css/main.css
http://localhost:8080/static/js/app.js
http://localhost:8080/static/images/logo.png

Subdirectory Support

http://localhost:8080/assets/css/main.css
http://localhost:8080/docs/api.html
http://localhost:8080/images/gallery/photo1.jpg

Index File Handling

BWS automatically serves index.html for directory requests:

http://localhost:8080/           โ†’ static/index.html
http://localhost:8080/docs/      โ†’ static/docs/index.html
http://localhost:8080/blog/      โ†’ static/blog/index.html

Directory Structure Examples

Basic Website

static/
โ”œโ”€โ”€ index.html          # Main page
โ”œโ”€โ”€ about.html          # About page
โ”œโ”€โ”€ styles.css          # Stylesheet
โ”œโ”€โ”€ script.js          # JavaScript
โ””โ”€โ”€ favicon.ico        # Site icon

Advanced Structure

static/
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ assets/
โ”‚   โ”œโ”€โ”€ css/
โ”‚   โ”‚   โ”œโ”€โ”€ main.css
โ”‚   โ”‚   โ”œโ”€โ”€ theme.css
โ”‚   โ”‚   โ””โ”€โ”€ responsive.css
โ”‚   โ”œโ”€โ”€ js/
โ”‚   โ”‚   โ”œโ”€โ”€ app.js
โ”‚   โ”‚   โ”œโ”€โ”€ utils.js
โ”‚   โ”‚   โ””โ”€โ”€ components/
โ”‚   โ”‚       โ”œโ”€โ”€ header.js
โ”‚   โ”‚       โ””โ”€โ”€ footer.js
โ”‚   โ”œโ”€โ”€ images/
โ”‚   โ”‚   โ”œโ”€โ”€ logo.svg
โ”‚   โ”‚   โ”œโ”€โ”€ hero.webp
โ”‚   โ”‚   โ””โ”€โ”€ gallery/
โ”‚   โ”‚       โ”œโ”€โ”€ photo1.jpg
โ”‚   โ”‚       โ””โ”€โ”€ photo2.jpg
โ”‚   โ””โ”€โ”€ fonts/
โ”‚       โ”œโ”€โ”€ Inter-Regular.woff2
โ”‚       โ””โ”€โ”€ Inter-Bold.woff2
โ”œโ”€โ”€ docs/
โ”‚   โ”œโ”€โ”€ index.html
โ”‚   โ”œโ”€โ”€ api.html
โ”‚   โ””โ”€โ”€ guide.html
โ””โ”€โ”€ downloads/
    โ”œโ”€โ”€ manual.pdf
    โ””โ”€โ”€ software.zip

Caching and Performance

BWS automatically adds caching headers:

Cache-Control: public, max-age=3600
Content-Type: text/css
Content-Length: 1234

Cache Control

  • Static files: 1 hour cache by default
  • HTML files: Shorter cache for dynamic content
  • Assets: Longer cache for images, fonts, etc.

Security Features

Path Traversal Protection

BWS prevents directory traversal attacks:

http://localhost:8080/../../../etc/passwd  โŒ Blocked
http://localhost:8080/..%2F..%2Fetc%2Fpasswd  โŒ Blocked

File Type Restrictions

Only serves files from the configured static_dir:

http://localhost:8080/config.toml  โŒ Not in static_dir
http://localhost:8080/.env         โŒ Hidden files blocked

Configuration Examples

Single Site

[[sites]]
name = "website"
hostname = "localhost"
port = 8080
static_dir = "public"

[sites.headers]
"Cache-Control" = "public, max-age=86400"
"X-Content-Type-Options" = "nosniff"

Multiple Asset Directories

# Main site
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "dist"

# CDN-like asset server
[[sites]]
name = "assets"
hostname = "assets.localhost"
port = 8081
static_dir = "assets"

[sites.headers]
"Cache-Control" = "public, max-age=31536000"
"Access-Control-Allow-Origin" = "*"

Development vs Production

# Development
[[sites]]
name = "dev"
hostname = "localhost"
port = 8080
static_dir = "src"

[sites.headers]
"Cache-Control" = "no-cache, no-store, must-revalidate"
"X-Environment" = "development"

# Production
[[sites]]
name = "prod"
hostname = "example.com"
port = 8080
static_dir = "build"

[sites.headers]
"Cache-Control" = "public, max-age=31536000"
"X-Environment" = "production"

Testing Static Files

Basic File Serving

# Test HTML file
curl -I http://localhost:8080/index.html

# Test CSS file
curl -I http://localhost:8080/styles.css

# Test JavaScript
curl -I http://localhost:8080/app.js

MIME Type Verification

# Check MIME type headers
curl -I http://localhost:8080/image.png | grep "Content-Type"
curl -I http://localhost:8080/app.wasm | grep "Content-Type"

Subdirectory Access

# Test nested files
curl -I http://localhost:8080/assets/css/main.css
curl -I http://localhost:8080/docs/api.html

Index File Testing

# Directory with trailing slash
curl -I http://localhost:8080/docs/

# Directory without trailing slash
curl -I http://localhost:8080/docs

Troubleshooting

File Not Found (404)

  • Check file exists in static_dir
  • Verify file permissions (readable)
  • Check path spelling and case sensitivity

Wrong MIME Type

  • File extension not recognized
  • Add custom MIME type mapping if needed
  • Verify file extension is correct

Caching Issues

  • Clear browser cache
  • Check Cache-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

Reverse Proxy

BWS provides comprehensive reverse proxy functionality with load balancing and WebSocket support.

Basic Configuration

[[sites]]
name = "proxy"
hostname = "api.example.com"
port = 80
static_dir = "static"

[sites.proxy]
enabled = true

# Backend servers
[[sites.proxy.upstreams]]
name = "backend"
url = "http://127.0.0.1:3001"
weight = 1

[[sites.proxy.upstreams]]
name = "backend" 
url = "http://127.0.0.1:3002"
weight = 2

# Routes
[[sites.proxy.routes]]
path = "/api/"
upstream = "backend"
strip_prefix = false

# Load balancing
[sites.proxy.load_balancing]
method = "round_robin"

Load Balancing Methods

Round Robin

[sites.proxy.load_balancing]
method = "round_robin"    # Distribute requests evenly

Weighted Round Robin

[[sites.proxy.upstreams]]
name = "backend"
url = "http://server1:3001"
weight = 3               # Gets 3x more requests

[[sites.proxy.upstreams]]
name = "backend"
url = "http://server2:3001"
weight = 1               # Gets 1x requests

[sites.proxy.load_balancing]
method = "weighted"

Least Connections

[sites.proxy.load_balancing]
method = "least_connections"  # Route to server with fewest active connections

Route Configuration

Path-Based Routing

# API routes to backend
[[sites.proxy.routes]]
path = "/api/"
upstream = "api-backend"
strip_prefix = false     # Keep /api/ in forwarded path

# Admin routes to different backend
[[sites.proxy.routes]]
path = "/admin/"
upstream = "admin-backend"
strip_prefix = true      # Remove /admin/ from forwarded path

WebSocket Proxying

[[sites.proxy.routes]]
path = "/ws"
upstream = "websocket-backend"
websocket = true         # Enable WebSocket support
strip_prefix = false

[[sites.proxy.upstreams]]
name = "websocket-backend"
url = "http://127.0.0.1:3001"  # Will proxy WebSocket connections

Header Management

Automatic Headers

[sites.proxy.headers]
add_x_forwarded = true   # Add X-Forwarded-For, X-Forwarded-Proto
add_forwarded = true     # Add standard Forwarded header

Custom Headers

[sites.proxy.headers.add]
"X-API-Gateway" = "BWS"
"X-Request-ID" = "auto"

[sites.proxy.headers.remove]
"Server" = true
"X-Powered-By" = true

Timeout Configuration

[sites.proxy.timeout]
read = 30               # Read timeout in seconds
write = 30              # Write timeout in seconds
connect = 5             # Connection timeout in seconds

Complete Example

[server]
name = "BWS Proxy Server"

# Main site with mixed static/proxy
[[sites]]
name = "main"
hostname = "example.com"
port = 80
static_dir = "static"

[sites.proxy]
enabled = true

# API backend cluster
[[sites.proxy.upstreams]]
name = "api-cluster"
url = "http://api1.internal:3001"
weight = 2

[[sites.proxy.upstreams]]
name = "api-cluster"
url = "http://api2.internal:3001"
weight = 1

# WebSocket backend
[[sites.proxy.upstreams]]
name = "websocket-backend"
url = "http://ws.internal:3002"

# Route API requests
[[sites.proxy.routes]]
path = "/api/"
upstream = "api-cluster"
strip_prefix = false

# Route WebSocket connections
[[sites.proxy.routes]]
path = "/ws"
upstream = "websocket-backend"
websocket = true

# Load balancing
[sites.proxy.load_balancing]
method = "weighted"

# Timeouts
[sites.proxy.timeout]
read = 30
write = 30
connect = 5

# Headers
[sites.proxy.headers]
add_x_forwarded = true
add_forwarded = true

[sites.proxy.headers.add]
"X-Gateway" = "BWS"

Testing Proxy Setup

Start Backend Services

# Start test backends
python3 -m http.server 3001 &
python3 -m http.server 3002 &

# Or use Node.js
npx http-server -p 3001 &
npx http-server -p 3002 &

Test Load Balancing

# Test multiple requests to see load balancing
for i in {1..10}; do
  curl -H "X-Request-ID: $i" http://localhost/api/test
done

# Check which backend handled each request
curl -v http://localhost/api/test

WebSocket Testing

# Test WebSocket connection
npx wscat -c ws://localhost/ws

# Test through proxy
curl --upgrade websocket http://localhost/ws

Monitoring

Health Checks

# Check proxy status
curl http://localhost/api/health

# Monitor backend connectivity
curl -v http://localhost/api/test

Log Analysis

# Monitor proxy logs
tail -f /var/log/bws.log | grep proxy

# Check upstream connections
netstat -an | grep :3001

Production Considerations

High Availability

# Multiple backend servers
[[sites.proxy.upstreams]]
name = "production-api"
url = "http://api1.prod:3001"
weight = 1

[[sites.proxy.upstreams]]
name = "production-api"
url = "http://api2.prod:3001"
weight = 1

[[sites.proxy.upstreams]]
name = "production-api"
url = "http://api3.prod:3001"
weight = 1

# Conservative timeouts
[sites.proxy.timeout]
read = 60
write = 60
connect = 10

Security Headers

[sites.proxy.headers.add]
"X-Content-Type-Options" = "nosniff"
"X-Frame-Options" = "DENY"
"X-XSS-Protection" = "1; mode=block"

[sites.proxy.headers.remove]
"Server" = true
"X-Powered-By" = true

SSL Termination

# Terminate SSL at BWS, HTTP to backends
[[sites]]
name = "ssl-proxy"
hostname = "api.example.com"
port = 443
static_dir = "static"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["api.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"

[sites.proxy]
enabled = true

[[sites.proxy.upstreams]]
name = "backend"
url = "http://internal-api:3001"  # HTTP to internal backend

Load Balancing in BWS

BWS reverse proxy includes comprehensive load balancing functionality similar to Caddy, with three different algorithms available for both HTTP and WebSocket connections.

Load Balancing Algorithms

1. Round Robin (round_robin)

Distributes requests evenly across all available backend servers in a circular manner.

How it works:

  • Maintains a counter for each upstream group
  • Increments counter for each request
  • Selects server based on counter % number_of_servers
  • Provides fair distribution when all servers have equal capacity

Best for:

  • Servers with similar performance characteristics
  • Simple, predictable load distribution
  • Development and testing environments
  • WebSocket chat applications with similar server capacity

2. Weighted (weighted)

Distributes requests based on assigned weights, allowing some servers to handle more traffic.

How it works:

  • Each server has a weight value (default: 1)
  • Random selection weighted by server capacity
  • Higher weight = more requests
  • Uses fast random number generation for selection

Best for:

  • Servers with different capacities
  • Gradual traffic migration
  • Performance-based load distribution
  • WebSocket servers with varying performance characteristics

3. Least Connections (least_connections)

Routes requests to the server with the fewest active connections.

How it works:

  • Tracks active connections per server using atomic counters
  • Increments counter when request starts
  • Decrements counter when request completes
  • Always routes to server with minimum connections

Best for:

  • Long-running requests
  • Servers with varying response times
  • Optimal connection distribution
  • WebSocket connections with persistent sessions

WebSocket Load Balancing

BWS extends all load balancing algorithms to support WebSocket connections with the same efficiency and reliability as HTTP requests.

WebSocket-Specific Features

  • Automatic Protocol Detection: BWS detects WebSocket upgrade requests and applies load balancing seamlessly
  • URL Transformation: HTTP upstream URLs are automatically converted to WebSocket URLs
    • http://localhost:3001 โ†’ ws://localhost: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

  1. Set up backend servers on different ports (3001, 3002, 3003)
  2. Configure BWS with the load balancing configuration
  3. Make multiple requests to see distribution
  4. 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:

FeatureBWSCaddy
Round Robinโœ…โœ…
Weightedโœ…โœ…
Least Connectionsโœ…โœ…
Health Checks๐Ÿ”„ Plannedโœ…
Sticky Sessions๐Ÿ”„ Plannedโœ…
Circuit Breaker๐Ÿ”„ Plannedโœ…

Future Enhancements

  • Health Checks: Automatic detection of unhealthy servers
  • Sticky Sessions: Route requests from same client to same server
  • Circuit Breaker: Temporary removal of failing servers
  • Metrics: Detailed statistics on load balancing performance
  • Dynamic Configuration: Runtime modification of upstream servers

Hot Reload & Zero-Downtime Configuration Updates

BWS implements a production-grade master-worker architecture that enables true hot reloading of configuration without dropping connections or interrupting service. This feature is essential for production environments where service availability is critical.

Architecture Overview

BWS uses a master-worker process model similar to nginx, HAProxy, and other enterprise-grade web servers:

Master Process (PID 1234)
โ”œโ”€โ”€ Configuration monitoring
โ”œโ”€โ”€ Signal handling
โ”œโ”€โ”€ Worker lifecycle management
โ””โ”€โ”€ Graceful shutdown coordination

Worker Process (PID 5678)
โ”œโ”€โ”€ HTTP request handling
โ”œโ”€โ”€ SSL/TLS termination
โ”œโ”€โ”€ Proxy and load balancing
โ””โ”€โ”€ Static file serving

How It Works

  1. Master Process: Monitors configuration files and manages worker processes
  2. Worker Processes: Handle actual HTTP traffic and connections
  3. Hot Reload: Master spawns new workers with updated config while old workers gracefully finish
  4. Zero Downtime: No connection drops or service interruption during configuration updates

Master-Worker Architecture

Master Process Responsibilities

  • Monitor configuration file changes
  • Handle reload signals (SIGHUP)
  • Spawn new worker processes
  • Gracefully terminate old workers
  • Coordinate shutdown procedures

Worker Process Responsibilities

  • Process HTTP requests
  • Handle SSL/TLS connections
  • Serve static files
  • Proxy requests to backends
  • Execute middleware

Process Lifecycle

graph TD
    A[Master Starts] --> B[Spawn Initial Worker]
    B --> C[Worker Serves Traffic]
    C --> D[SIGHUP Received]
    D --> E[Load New Config]
    E --> F[Validate Config]
    F --> G[Spawn New Worker]
    G --> H[New Worker Ready]
    H --> I[Graceful Old Worker Shutdown]
    I --> C

Configuration Hot Reload

Triggering Hot Reload

Hot reload can be triggered in several ways:

Using Unix Signals

# Find master process
pgrep -f "bws.*master"

# Send SIGHUP for hot reload
kill -HUP $(pgrep -f "bws.*master")

Using systemd

# Reload configuration through systemd
sudo systemctl reload bws

Using Process Management

# Direct signal to specific PID
kill -HUP 1234

What Can Be Hot Reloaded

โœ… Supported (Zero Downtime):

  • Site configurations
  • Hostname mappings
  • Multi-hostname configurations
  • SSL certificate paths
  • ACME settings
  • Proxy upstream configurations
  • Load balancing algorithms
  • Static file directories
  • Security headers
  • Middleware configurations
  • Logging settings
  • Health check configurations

โŒ Not Supported (Requires Restart):

  • Server listen ports
  • Worker process count
  • Core server settings
  • TLS protocol versions

Configuration Validation

Before applying changes, BWS validates the new configuration:

# The master process automatically:
# 1. Loads new configuration file
# 2. Validates syntax and semantics
# 3. Checks file permissions and paths
# 4. Verifies SSL certificate validity
# 5. Tests upstream connectivity (optional)

If validation fails, the master process keeps the existing configuration and logs an error.

Hot Reload Process

Step-by-Step Flow

  1. Signal Reception

    Master Process: Received SIGHUP signal
    
  2. Configuration Loading

    Master Process: Loading configuration from /etc/bws/config.toml
    
  3. Validation

    Master Process: Validating new configuration
    
  4. Worker Spawning

    Master Process: Spawning new worker with PID 9876
    
  5. Traffic Transition

    New Worker: Ready to accept connections
    
  6. Graceful Shutdown

    Master Process: Sending SIGTERM to old worker (PID 5678)
    Old Worker: Finishing existing connections
    Old Worker: Graceful shutdown complete
    

Error Handling

If hot reload fails:

  • Invalid configuration: Master logs error and keeps current config
  • Worker spawn failure: Master retries and falls back to current worker
  • Resource exhaustion: Master logs warning and maintains current worker

Configuration Examples

Basic Hot Reload Setup

# config.toml
[server]
name = "BWS Production Server"

[[sites]]
name = "main-site"
hostname = "example.com"
port = 8080
static_dir = "/var/www/html"

# Add new site without restart
[[sites]]
name = "api-site"
hostname = "api.example.com"
port = 8080
static_dir = "/var/www/api"

[sites.proxy]
enabled = true

[[sites.proxy.upstreams]]
name = "backend"
url = "http://192.168.1.100:3000"

Multi-Hostname Hot Reload

[[sites]]
name = "company-main"
hostname = "company.com"
# Add new hostnames without restart
hostnames = [
    "www.company.com",
    "company.net",      # New hostname
    "www.company.net"   # New hostname
]
port = 8080
static_dir = "/var/www/company"

SSL Configuration Hot Reload

[[sites]]
name = "secure-site"
hostname = "secure.example.com"
port = 443

[sites.ssl]
enabled = true
# Update certificate paths without restart
cert_file = "/etc/ssl/certs/new-cert.pem"
key_file = "/etc/ssl/private/new-key.pem"

[sites.ssl.acme]
enabled = true
# Update ACME settings without restart
email = "security@example.com"

Proxy Configuration Hot Reload

[[sites]]
name = "proxy-site"
hostname = "proxy.example.com"
port = 8080

[sites.proxy]
enabled = true

# Add new upstreams without restart
[[sites.proxy.upstreams]]
name = "backend-1"
url = "http://192.168.1.101:3000"
weight = 3

[[sites.proxy.upstreams]]
name = "backend-2"
url = "http://192.168.1.102:3000"
weight = 2

# Update routing without restart
[[sites.proxy.routes]]
path = "/api/v1/"
upstream = "backend-1"

[[sites.proxy.routes]]
path = "/api/v2/"
upstream = "backend-2"

Monitoring Hot Reload

Process Monitoring

# View process tree
pstree -p $(pgrep -f "bws.*master")

# Monitor process status
watch "ps aux | grep bws"

# Check process relationships
ps -eo pid,ppid,cmd | grep bws

Log Monitoring

# Follow hot reload logs
tail -f /var/log/bws/bws.log | grep -E "(reload|worker|master)"

# Filter for specific events
journalctl -u bws -f | grep "Hot reload"

Health Checking

# Verify service is responding
curl -I http://localhost:8080/health

# Check configuration version
curl -s http://localhost:8080/api/health | jq '.config_version'

# Test new configuration features
curl -H "Host: new-site.com" http://localhost:8080/

Production Considerations

Performance Impact

  • Worker Spawn Time: ~100-500ms depending on configuration complexity
  • Memory Usage: Brief spike during worker transition
  • CPU Impact: Minimal, mostly during config validation
  • Connection Handling: Zero dropped connections

Best Practices

  1. Configuration Validation

    # Always validate before reload
    bws --config-check /etc/bws/config.toml
    
  2. Gradual Rollouts

    # Test on staging first
    # Use monitoring to verify health
    # Roll back quickly if issues arise
    
  3. Monitoring Setup

    # Monitor process counts
    # Track memory usage
    # Alert on failed reloads
    
  4. Backup Configurations

    # Version control configurations
    # Keep rollback configs ready
    # Document all changes
    

Systemd Integration

# /etc/systemd/system/bws.service
[Unit]
Description=BWS Web Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Automated Hot Reload

#!/bin/bash
# hot-reload-on-config-change.sh

CONFIG_FILE="/etc/bws/config.toml"
MASTER_PID=$(pgrep -f "bws.*master")

# Watch for config file changes
inotifywait -m -e modify "$CONFIG_FILE" | while read event; do
    echo "Configuration file changed, triggering hot reload..."
    
    # Validate configuration
    if bws --config-check "$CONFIG_FILE"; then
        echo "Configuration valid, sending SIGHUP"
        kill -HUP "$MASTER_PID"
    else
        echo "Configuration invalid, skipping reload"
    fi
done

Troubleshooting

Common Issues

Master Process Not Found

# Check if BWS is running
systemctl status bws

# Look for all BWS processes
ps aux | grep bws

Configuration Not Reloading

# Check configuration syntax
bws --config-check /etc/bws/config.toml

# Verify file permissions
ls -la /etc/bws/config.toml

# Check logs for errors
journalctl -u bws | tail -20

Worker Process Issues

# Check process tree
pstree -p $(pgrep -f "bws.*master")

# Monitor worker spawning
tail -f /var/log/bws/bws.log | grep worker

Debug Mode

# Start with verbose logging
RUST_LOG=debug bws --config config.toml

# Enable hot reload debugging
RUST_LOG=bws::core::hot_reload=trace bws --config config.toml

Recovery Procedures

Failed Hot Reload

# Check current configuration
curl http://localhost:8080/api/health

# Restart if necessary
systemctl restart bws

Stuck Worker Process

# Force kill unresponsive worker
kill -9 $(pgrep -f "bws.*worker")

# Master will spawn new worker automatically

Integration Examples

With Load Balancer

# HAProxy health check during reload
backend bws_servers
    option httpchk GET /health
    server bws1 127.0.0.1:8080 check

With Monitoring

# Prometheus metrics for hot reload events
bws_hot_reload_total{status="success"} 15
bws_hot_reload_total{status="failed"} 1
bws_worker_spawn_duration_seconds 0.342

With CI/CD Pipeline

# Deploy script
- name: Update BWS Configuration
  run: |
    # Validate new config
    bws --config-check new-config.toml
    
    # Deploy config
    cp new-config.toml /etc/bws/config.toml
    
    # Hot reload
    systemctl reload bws
    
    # Verify deployment
    curl -f http://localhost:8080/health

Next Steps

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

  1. Open DevTools (F12)
  2. Navigate to Network tab
  3. Reload page
  4. Click on any request
  5. 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

  1. Check TOML syntax in config file
  2. Restart BWS after configuration changes
  3. Verify header names are correct (case-sensitive)
  4. 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

  1. Start with permissive policy
  2. Use browser console to identify violations
  3. Gradually restrict policy
  4. 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

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

Security

BWS provides comprehensive security features to protect your web applications and server infrastructure.

Management API Security

BWS includes a secure Management API for administrative operations like configuration reloading. This API is designed with security-first principles.

Localhost-Only Access

The Management API runs on a separate port and binds exclusively to localhost (127.0.0.1) to prevent external access:

[management]
enabled = true
host = "127.0.0.1"    # Localhost only - cannot be changed
port = 7654           # Configurable port

Security Benefits:

  • ๐Ÿ”’ No External Access: Only accessible from the server itself
  • ๐Ÿ”’ Isolated Service: Separate from main web server ports
  • ๐Ÿ”’ IP Validation: Double-checks request origin is localhost

API Key Authentication

For additional security, you can enable API key authentication:

[management]
enabled = true
host = "127.0.0.1"
port = 7654
api_key = "your-secure-api-key-here"

Usage with API Key:

# Config reload with API key
curl -X POST http://127.0.0.1:7654/api/config/reload \
  -H "X-API-Key: your-secure-api-key-here"

Without API Key:

# Config reload (localhost only)
curl -X POST http://127.0.0.1:7654/api/config/reload

Available Endpoints

EndpointMethodDescriptionAuthentication
/api/config/reloadPOSTReload configurationOptional API Key

Security Logging

All Management API requests are logged with client IP addresses:

[INFO] Management API: Config reload requested
[INFO] Configuration reloaded successfully via management API
[INFO] Management API request: POST /api/config/reload from 127.0.0.1:35528

General Security Features

Request Size Limits

Protect against oversized requests:

[security]
max_request_size = "10MB"

HTTPS/TLS Support

BWS supports modern TLS configurations:

[sites.ssl]
enabled = true
cert_path = "./certs/domain.crt"
key_path = "./certs/domain.key"
auto_cert = true  # ACME/Let's Encrypt support

Custom Security Headers

Add security headers to responses:

[sites.headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"
"Content-Security-Policy" = "default-src 'self'"

Access Control

Configure CORS and access control:

[sites.access_control]
enabled = true
allow_origins = ["https://example.com", "https://app.example.com"]
allow_methods = ["GET", "POST", "PUT", "DELETE"]
allow_headers = ["Content-Type", "Authorization"]
max_age = 3600

Production Security Checklist

Management API

  • Enable API key authentication in production
  • Ensure port 7654 is not exposed externally via firewall
  • Monitor management API access logs
  • Rotate API keys regularly

General Security

  • Use HTTPS/TLS for all sites
  • Configure appropriate security headers
  • Set reasonable request size limits
  • Enable access logging
  • Regular security updates

Network Security

  • Configure firewall rules
  • Use reverse proxy if needed
  • Monitor unusual traffic patterns
  • Implement rate limiting if required

Security Best Practices

  1. Principle of Least Privilege: Only enable features you need
  2. Defense in Depth: Use multiple security layers
  3. Regular Updates: Keep BWS and dependencies updated
  4. Monitoring: Enable comprehensive logging
  5. Testing: Regularly test security configurations

Reporting Security Issues

If you discover a security vulnerability in BWS, please report it responsibly:

  1. Do not open a public GitHub issue
  2. Contact the maintainers privately
  3. Provide detailed reproduction steps
  4. Allow time for the issue to be fixed before disclosure

Your security reports help make BWS safer for everyone.

Docker Deployment

BWS provides official Docker images for easy deployment.

Quick Start

Run with Default Configuration

# Run BWS on port 8080
docker run -p 8080:8080 ghcr.io/benliao/bws:latest

Run with Custom Configuration

# Run with custom config and static files
docker run -d \
  --name bws \
  -p 8080:8080 \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  -v $(pwd)/static:/app/static:ro \
  ghcr.io/benliao/bws:latest

Available Images

Docker Hub Tags

  • latest - Latest stable release
  • v0.3.5 - Specific version (recommended for production)
  • main - Latest development build

Multi-Architecture Support

Images support multiple architectures:

  • linux/amd64 (x86_64)
  • linux/arm64 (ARM64/Apple Silicon)

Configuration Examples

Single Static Site

# Create content
mkdir static
echo "<h1>Hello from Docker!</h1>" > static/index.html

# Run container
docker run -d \
  --name bws-static \
  -p 8080:8080 \
  -v $(pwd)/static:/app/static:ro \
  ghcr.io/benliao/bws:latest \
  bws /app/static --port 8080

Multi-Site with SSL

# Create configuration
cat > config.toml << 'EOF'
[server]
name = "Docker Multi-Site"

[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "/app/static"
default = true

[[sites]]
name = "secure"
hostname = "secure.localhost"
port = 8443
static_dir = "/app/static"

[sites.ssl]
enabled = true
auto_cert = true
domains = ["secure.localhost"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
challenge_dir = "/app/acme-challenges"
EOF

# Create directories
mkdir -p static acme-challenges

# Run container
docker run -d \
  --name bws-multisite \
  -p 8080:8080 \
  -p 8443:8443 \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  -v $(pwd)/static:/app/static:ro \
  -v $(pwd)/acme-challenges:/app/acme-challenges \
  ghcr.io/benliao/bws:latest

Docker Compose

Basic Setup

# docker-compose.yml
version: '3.8'

services:
  bws:
    image: ghcr.io/benliao/bws:latest
    container_name: bws
    ports:
      - "8080:8080"
      - "8443:8443"
    volumes:
      - ./config.toml:/app/config.toml:ro
      - ./static:/app/static:ro
      - ./acme-challenges:/app/acme-challenges
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3

Production Setup with Reverse Proxy

version: '3.8'

services:
  bws:
    image: ghcr.io/benliao/bws:v0.3.5
    container_name: bws
    expose:
      - "8080"
    volumes:
      - ./config.toml:/app/config.toml:ro
      - ./static:/app/static:ro
      - ./acme-challenges:/app/acme-challenges
    restart: unless-stopped
    networks:
      - bws-network

  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl-certs:/etc/ssl/certs:ro
    depends_on:
      - bws
    restart: unless-stopped
    networks:
      - bws-network

networks:
  bws-network:
    driver: bridge

Start with compose:

docker-compose up -d

Build Custom Image

Dockerfile

FROM ghcr.io/benliao/bws:latest

# Copy custom configuration
COPY config.toml /app/config.toml
COPY static/ /app/static/

# Expose ports
EXPOSE 8080 8443

# Run BWS
CMD ["bws", "--config", "/app/config.toml"]

Build and Run

# Build custom image
docker build -t my-bws .

# Run custom image
docker run -d --name my-bws-container -p 8080:8080 my-bws

Volume Mounts

Important Directories

  • /app/config.toml - Configuration file
  • /app/static/ - Static content directory
  • /app/acme-challenges/ - ACME challenge directory
  • /app/certs/ - Manual SSL certificates

Example with All Mounts

docker run -d \
  --name bws-full \
  -p 80:80 \
  -p 443:443 \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  -v $(pwd)/static:/app/static:ro \
  -v $(pwd)/certs:/app/certs:ro \
  -v $(pwd)/acme-challenges:/app/acme-challenges \
  -v $(pwd)/logs:/app/logs \
  ghcr.io/benliao/bws:latest

Environment Variables

# Override config with environment variables
docker run -d \
  --name bws-env \
  -p 8080:8080 \
  -e BWS_LOG_LEVEL=debug \
  -e BWS_WORKERS=4 \
  -v $(pwd)/static:/app/static:ro \
  ghcr.io/benliao/bws:latest \
  bws /app/static --port 8080

Health Checks

Docker Health Check

# Run with health check
docker run -d \
  --name bws-health \
  -p 8080:8080 \
  --health-cmd="curl -f http://localhost:8080/api/health || exit 1" \
  --health-interval=30s \
  --health-timeout=10s \
  --health-retries=3 \
  ghcr.io/benliao/bws:latest

Check Container Health

# Check health status
docker ps
docker inspect bws-health | grep -A 10 '"Health"'

Troubleshooting

Common Issues

Container Won't Start:

# Check logs
docker logs bws

# Validate configuration
docker run --rm \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  ghcr.io/benliao/bws:latest \
  bws --config /app/config.toml --dry-run

Permission Issues:

# Fix volume permissions
sudo chown -R 1000:1000 static/ acme-challenges/

Port Conflicts:

# Check port usage
docker ps -a
netstat -tulpn | grep :8080

Debug Commands

# Run interactive shell
docker run -it --entrypoint /bin/sh ghcr.io/benliao/bws:latest

# Exec into running container
docker exec -it bws /bin/sh

# View container logs
docker logs -f bws

Security

Best Practices

  • Use specific version tags (not latest) in production
  • Mount configuration as read-only (:ro)
  • Run as non-root user (default in BWS image)
  • Use Docker secrets for sensitive data

Non-Root User

# BWS image runs as user 1000 by default
docker run -d \
  --name bws-secure \
  --user 1000:1000 \
  -p 8080:8080 \
  ghcr.io/benliao/bws:latest

Daemon Mode Configuration

BWS can run as a system daemon (background service) for production deployments, providing automatic startup, monitoring, and management capabilities.

Master-Worker Architecture

BWS uses a master-worker process model for production deployments:

Process Model

  • Master Process: Manages configuration, handles signals, spawns workers
  • Worker Processes: Handle HTTP traffic, SSL termination, proxying
  • Hot Reload: Zero-downtime configuration updates via worker replacement
  • Graceful Shutdown: Coordinated shutdown with connection draining

Signal Handling

BWS master process responds to standard Unix signals:

# Hot reload configuration (spawn new worker)
sudo kill -HUP $(pgrep -f "bws.*master")

# Graceful shutdown (stop all processes)
sudo kill -TERM $(pgrep -f "bws.*master")

# Check process tree
pstree -p $(pgrep -f "bws.*master")

Systemd Configuration

Service File Creation

Create a systemd service file for BWS:

# /etc/systemd/system/bws.service
[Unit]
Description=BWS Multi-Site Web Server
Documentation=https://github.com/yourusername/bws
After=network.target
Wants=network.target

[Service]
Type=simple
User=bws
Group=bws
WorkingDirectory=/opt/bws
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
TimeoutStartSec=60
TimeoutStopSec=30

# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/bws /var/lib/bws

# Resource limits
LimitNOFILE=65536
LimitNPROC=4096

# Environment
Environment=RUST_LOG=info
Environment=BWS_CONFIG=/etc/bws/config.toml
Environment=BWS_LOG_FILE=/var/log/bws/bws.log
Environment=BWS_PID_FILE=/var/run/bws.pid

[Install]
WantedBy=multi-user.target

Installing the Service

# Copy service file
sudo cp bws.service /etc/systemd/system/

# Reload systemd
sudo systemctl daemon-reload

# Enable service (auto-start on boot)
sudo systemctl enable bws

# Start service
sudo systemctl start bws

# Check status
sudo systemctl status bws

Service Management

# Start the service
sudo systemctl start bws

# Stop the service
sudo systemctl stop bws

# Restart the service
sudo systemctl restart bws

# Reload configuration
sudo systemctl reload bws

# Check service status
sudo systemctl status bws

# View logs
sudo journalctl -u bws -f

# Check if service is enabled
sudo systemctl is-enabled bws

User and Directory Setup

Creating BWS User

# Create system user for BWS
sudo useradd -r -s /bin/false -d /opt/bws bws

# Create necessary directories
sudo mkdir -p /opt/bws
sudo mkdir -p /etc/bws
sudo mkdir -p /var/log/bws
sudo mkdir -p /var/lib/bws

# Set ownership
sudo chown -R bws:bws /opt/bws
sudo chown -R bws:bws /var/log/bws
sudo chown -R bws:bws /var/lib/bws
sudo chown root:bws /etc/bws

# Set permissions
sudo chmod 755 /opt/bws
sudo chmod 750 /etc/bws
sudo chmod 755 /var/log/bws
sudo chmod 755 /var/lib/bws

File Structure

/opt/bws/                 # BWS home directory
โ”œโ”€โ”€ static/               # Static files
โ”œโ”€โ”€ sites/                # Multi-site configurations
โ””โ”€โ”€ bin/                  # BWS binary (optional)

/etc/bws/                 # Configuration directory
โ”œโ”€โ”€ config.toml           # Main configuration
โ”œโ”€โ”€ sites/                # Site-specific configs
โ””โ”€โ”€ ssl/                  # SSL certificates

/var/log/bws/             # Log directory
โ”œโ”€โ”€ bws.log               # Main log file
โ”œโ”€โ”€ access.log            # Access logs
โ””โ”€โ”€ error.log             # Error logs

/var/lib/bws/             # Runtime data
โ”œโ”€โ”€ cache/                # Cache files
โ””โ”€โ”€ temp/                 # Temporary files

Configuration Files

Main Configuration

# /etc/bws/config.toml
[daemon]
user = "bws"
group = "bws"
pid_file = "/var/run/bws.pid"
working_directory = "/opt/bws"

[logging]
level = "info"
output = "file"
file_path = "/var/log/bws/bws.log"
max_size = "100MB"
max_files = 10
compress = true

[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "/opt/bws/static"

[sites.headers]
"X-Served-By" = "BWS"
"Cache-Control" = "public, max-age=3600"

Environment Configuration

# /etc/bws/environment
BWS_CONFIG=/etc/bws/config.toml
BWS_LOG_FILE=/var/log/bws/bws.log
BWS_PID_FILE=/var/run/bws.pid
RUST_LOG=info
RUST_BACKTRACE=1

Process Management

Signal Handling

BWS master process responds to standard Unix signals:

# Hot reload configuration (spawn new worker)
sudo kill -HUP $(pgrep -f "bws.*master")

# Graceful shutdown (stop all processes)
sudo kill -TERM $(pgrep -f "bws.*master")

# Check process tree  
pstree -p $(pgrep -f "bws.*master")

Master-Worker Operations

# View all BWS processes
ps aux | grep bws

# Check master process
pgrep -f "bws.*master"

# Monitor worker processes
pgrep -f "bws.*worker"

# Hot reload without downtime
systemctl reload bws

Process Monitoring

# Check if BWS is running
pgrep -f bws

# Monitor BWS process
ps aux | grep bws

# Check open files
sudo lsof -p $(cat /var/run/bws.pid)

# Monitor resource usage
top -p $(cat /var/run/bws.pid)

Log Management

Log Rotation Configuration

# /etc/logrotate.d/bws
/var/log/bws/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    copytruncate
    postrotate
        systemctl reload bws
    endscript
}

Log Monitoring

# Follow main log
tail -f /var/log/bws/bws.log

# Follow with filtering
tail -f /var/log/bws/bws.log | grep ERROR

# Search logs
grep "error" /var/log/bws/bws.log

# Count log entries by level
grep -c "INFO\|WARN\|ERROR" /var/log/bws/bws.log

Monitoring and Health Checks

Health Check Script

#!/bin/bash
# /usr/local/bin/bws-health-check
BWS_PID_FILE="/var/run/bws.pid"
BWS_HEALTH_URL="http://localhost:8080/health"

# Check if PID file exists
if [ ! -f "$BWS_PID_FILE" ]; then
    echo "ERROR: PID file not found"
    exit 1
fi

# Check if process is running
PID=$(cat "$BWS_PID_FILE")
if ! kill -0 "$PID" 2>/dev/null; then
    echo "ERROR: BWS process not running"
    exit 1
fi

# Check health endpoint
if ! curl -f -s "$BWS_HEALTH_URL" > /dev/null; then
    echo "ERROR: Health check failed"
    exit 1
fi

echo "OK: BWS is healthy"
exit 0

Monitoring with Cron

# Add to crontab for user bws
*/5 * * * * /usr/local/bin/bws-health-check || /usr/bin/logger "BWS health check failed"

Systemd Timer for Health Checks

# /etc/systemd/system/bws-health.service
[Unit]
Description=BWS Health Check
After=bws.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/bws-health-check
User=bws
# /etc/systemd/system/bws-health.timer
[Unit]
Description=BWS Health Check Timer
Requires=bws-health.service

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target

Auto-Recovery and Restart

Automatic Restart Configuration

# Enhanced systemd service with restart logic
[Service]
Type=simple
Restart=always
RestartSec=5
StartLimitInterval=60
StartLimitBurst=3

# Restart conditions
RestartPreventExitStatus=1 2 3 4 6 SIGTERM

Recovery Script

#!/bin/bash
# /usr/local/bin/bws-recovery
LOG_FILE="/var/log/bws/recovery.log"
PID_FILE="/var/run/bws.pid"

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}

# Check if BWS is running
if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
    if curl -f -s http://localhost:8080/health > /dev/null; then
        log_message "BWS is healthy"
        exit 0
    fi
fi

log_message "BWS appears to be down, attempting restart"

# Stop any existing processes
systemctl stop bws
sleep 5

# Clean up PID file if exists
[ -f "$PID_FILE" ] && rm -f "$PID_FILE"

# Start BWS
if systemctl start bws; then
    log_message "BWS restarted successfully"
    exit 0
else
    log_message "Failed to restart BWS"
    exit 1
fi

Security Considerations

Service Security

# Enhanced security in systemd service
[Service]
# Run as non-root user
User=bws
Group=bws

# Security restrictions
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/log/bws /var/lib/bws

# Capability restrictions
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

# Network restrictions
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX

# File system restrictions
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true

File Permissions

# Set secure permissions
chmod 600 /etc/bws/config.toml
chmod 755 /etc/bws
chmod 644 /usr/local/bin/bws
chmod 755 /usr/local/bin/bws

# Verify permissions
ls -la /etc/bws/
ls -la /var/log/bws/
ls -la /opt/bws/

Integration Examples

With Nginx

# /etc/nginx/sites-available/bws
upstream bws {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://bws;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /health {
        proxy_pass http://bws/health;
        access_log off;
    }
}

With Load Balancer

# HAProxy configuration
global
    daemon
    maxconn 4096

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend web_frontend
    bind *:80
    default_backend bws_servers

backend bws_servers
    balance roundrobin
    option httpchk GET /health
    server bws1 127.0.0.1:8080 check
    server bws2 127.0.0.1:8081 check

Troubleshooting

Service Won't Start

# Check service status
systemctl status bws

# View detailed logs
journalctl -u bws -xe

# Check configuration
bws --config-check /etc/bws/config.toml

# Verify permissions
ls -la /etc/bws/config.toml
ls -la /usr/local/bin/bws

Permission Errors

# Fix ownership
sudo chown -R bws:bws /opt/bws /var/log/bws

# Fix permissions
sudo chmod 755 /opt/bws
sudo chmod 644 /etc/bws/config.toml

# Check SELinux (if applicable)
sestatus
setsebool -P httpd_can_network_connect 1

Performance Issues

# Check resource limits
systemctl show bws | grep Limit

# Monitor system resources
htop
iotop
netstat -tulpn

Best Practices

Configuration Management

  • Store configurations in version control
  • Use configuration templates for different environments
  • Validate configurations before deployment
  • Document all configuration changes

Monitoring

  • Set up comprehensive logging
  • Monitor service health continuously
  • Configure alerting for service failures
  • Regular log analysis and cleanup

Security

  • Run with minimal privileges
  • Regular security updates
  • Secure file permissions
  • Network security (firewall rules)

Maintenance

  • Regular backup of configurations
  • Monitor disk space for logs
  • Plan for service updates
  • Document operational procedures

Next Steps

Production Setup

Deploy BWS in production with security, performance, and reliability best practices.

Quick Production Setup

1. Install BWS

# Install from crates.io
cargo install bws-web-server

# Or use Docker
docker pull ghcr.io/benliao/bws:latest

2. Create Production Configuration

[server]
name = "BWS Production"

# Main HTTPS site
[[sites]]
name = "main"
hostname = "example.com"
port = 443
static_dir = "/var/www/html"
default = true

[sites.ssl]
enabled = true
auto_cert = true
domains = ["example.com", "www.example.com"]

[sites.ssl.acme]
enabled = true
email = "admin@example.com"
staging = false

[sites.headers]
"Strict-Transport-Security" = "max-age=31536000"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"

# Management API
[management]
enabled = true
port = 7654
api_key = "secure-random-key-here"

# Performance tuning
[performance]
worker_threads = 8
max_connections = 2000

# Security
[security]
hide_server_header = true
max_request_size = "50MB"

# Logging
[logging]
level = "info"
log_requests = true

3. System Setup

# Create user and directories
sudo useradd -r -s /bin/false bws
sudo mkdir -p /var/www/html /var/log/bws /etc/bws
sudo chown -R bws:bws /var/www /var/log/bws

# Copy configuration
sudo cp config.toml /etc/bws/

# Set permissions
sudo chmod 600 /etc/bws/config.toml
sudo chown bws:bws /etc/bws/config.toml

Systemd Service

Create Service File

# /etc/systemd/system/bws.service
[Unit]
Description=BWS Web Server
After=network.target
Wants=network.target

[Service]
Type=forking
User=bws
Group=bws
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml --daemon
ExecReload=/bin/kill -HUP $MAINPID
PIDFile=/var/run/bws.pid
Restart=always
RestartSec=5
LimitNOFILE=65535

# Security
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/www /var/log/bws /var/run

[Install]
WantedBy=multi-user.target

Service Management

# Enable and start service
sudo systemctl enable bws
sudo systemctl start bws

# Check status
sudo systemctl status bws

# View logs
sudo journalctl -u bws -f

# Reload configuration
sudo systemctl reload bws

Docker Production Deployment

Docker Compose

# docker-compose.prod.yml
version: '3.8'

services:
  bws:
    image: ghcr.io/benliao/bws:v0.3.5
    container_name: bws-prod
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./config.toml:/app/config.toml:ro
      - ./static:/app/static:ro
      - ./acme-challenges:/app/acme-challenges
      - ./logs:/app/logs
    environment:
      - BWS_LOG_LEVEL=info
    networks:
      - bws-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3

networks:
  bws-network:
    driver: bridge

Deploy with Docker

# Start production stack
docker-compose -f docker-compose.prod.yml up -d

# View logs
docker-compose -f docker-compose.prod.yml logs -f

# Update deployment
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d

Reverse Proxy Setup

Nginx Frontend (Optional)

# /etc/nginx/sites-available/bws
upstream bws_backend {
    server 127.0.0.1:8080;
    keepalive 32;
}

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://bws_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Security

Firewall Configuration

# UFW setup
sudo ufw allow 22/tcp      # SSH
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS
sudo ufw enable

# Restrict management API
sudo ufw allow from 127.0.0.1 to any port 7654

SSL/TLS Security

# Strong SSL configuration
[sites.ssl]
enabled = true
auto_cert = true
min_tls_version = "1.2"

[sites.headers]
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains; preload"
"Content-Security-Policy" = "default-src 'self'"
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"Referrer-Policy" = "strict-origin-when-cross-origin"

Monitoring

Health Checks

# Basic health check
curl -f http://localhost/api/health

# Detailed health information
curl http://localhost/api/health/detailed

# Check SSL certificate
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

Log Monitoring

# Monitor access logs
tail -f /var/log/bws/access.log

# Monitor error logs
tail -f /var/log/bws/error.log

# System resource monitoring
htop
iostat -x 1

Alerting Setup

# Check certificate expiration
#!/bin/bash
# check-ssl.sh
DOMAIN="example.com"
EXPIRY=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_DATE=$(date -d "$EXPIRY" +%s)
CURRENT_DATE=$(date +%s)
DAYS_UNTIL_EXPIRY=$(( ($EXPIRY_DATE - $CURRENT_DATE) / 86400 ))

if [ $DAYS_UNTIL_EXPIRY -lt 30 ]; then
    echo "SSL certificate for $DOMAIN expires in $DAYS_UNTIL_EXPIRY days!"
    # Send alert (email, Slack, etc.)
fi

Performance Tuning

System Limits

# /etc/security/limits.conf
bws soft nofile 65535
bws hard nofile 65535

# /etc/sysctl.conf
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 5000

BWS Configuration

[performance]
worker_threads = 16              # 2x CPU cores
max_connections = 10000          # Per worker
keep_alive_timeout = 75          # Seconds
request_timeout = 30             # Seconds
max_header_size = "16KB"

Backup and Recovery

Configuration Backup

# Backup script
#!/bin/bash
BACKUP_DIR="/backups/bws/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR

# Backup configuration
cp /etc/bws/config.toml $BACKUP_DIR/
cp -r /var/www $BACKUP_DIR/
cp -r /etc/letsencrypt $BACKUP_DIR/

# Create archive
tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR
rm -rf $BACKUP_DIR

Recovery Procedure

# Restore from backup
tar -xzf /backups/bws/20250827.tar.gz -C /

# Restore permissions
sudo chown -R bws:bws /var/www /etc/bws
sudo chmod 600 /etc/bws/config.toml

# Restart service
sudo systemctl restart bws

Troubleshooting

Common Issues

# Check BWS status
sudo systemctl status bws

# Validate configuration
bws --config /etc/bws/config.toml --dry-run

# Check logs
sudo journalctl -u bws --since "1 hour ago"

# Test configuration reload
curl -X POST http://127.0.0.1:7654/api/config/reload \
  -H "X-API-Key: your-api-key"

# Check process tree
pstree -p $(pgrep -f bws)

Performance Debugging

# Check resource usage
top -p $(pgrep bws)

# Network connections
ss -tulpn | grep bws

# File descriptors
lsof -p $(pgrep bws) | wc -l

Updates and Maintenance

Rolling Updates

# Download new version
cargo install bws-web-server --force

# Validate new binary
bws --version
bws --config /etc/bws/config.toml --dry-run

# Hot reload (zero downtime)
sudo systemctl reload bws

# Or restart if needed
sudo systemctl restart bws

Maintenance Window

# Graceful shutdown
sudo systemctl stop bws

# Perform maintenance
# - Update system packages
# - Update BWS configuration
# - Clean log files

# Start service
sudo systemctl start bws

# Verify operation
curl -f http://localhost/api/health

Performance Tuning

This guide covers optimizing BWS for maximum performance, throughput, and efficiency in production environments.

Performance Overview

BWS is built on Pingora, providing excellent performance characteristics:

  • High-performance async I/O
  • Minimal memory footprint
  • Efficient connection handling
  • Built-in caching capabilities
  • Horizontal scaling support

System-Level Optimization

Operating System Tuning

Network Stack Optimization

# /etc/sysctl.d/99-bws-performance.conf

# TCP/IP stack tuning
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 15

# Buffer sizes
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# Connection tracking
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 300

# Apply settings
sysctl -p /etc/sysctl.d/99-bws-performance.conf

File Descriptor Limits

# /etc/security/limits.d/bws-performance.conf
bws soft nofile 1048576
bws hard nofile 1048576
bws soft nproc 32768
bws hard nproc 32768

# For systemd services
# /etc/systemd/system/bws.service.d/limits.conf
[Service]
LimitNOFILE=1048576
LimitNPROC=32768

CPU and Memory Optimization

# CPU governor for performance
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# Disable swap for consistent performance
swapoff -a
# Comment out swap in /etc/fstab

# NUMA optimization (for multi-socket systems)
echo 0 > /proc/sys/kernel/numa_balancing

Storage Optimization

File System Tuning

# Mount options for static files (add to /etc/fstab)
/dev/sdb1 /opt/bws/static ext4 defaults,noatime,nodiratime,data=writeback 0 0

# For high-performance scenarios
/dev/nvme0n1 /opt/bws/cache xfs defaults,noatime,largeio,inode64 0 0

# Temporary files in memory
tmpfs /tmp tmpfs defaults,size=2G,mode=1777 0 0
tmpfs /var/tmp tmpfs defaults,size=1G,mode=1777 0 0

I/O Scheduler Optimization

# For SSDs
echo noop > /sys/block/sda/queue/scheduler

# For HDDs
echo deadline > /sys/block/sda/queue/scheduler

# Make persistent (add to /etc/rc.local or systemd service)
echo 'noop' > /sys/block/sda/queue/scheduler

BWS Configuration Optimization

Performance-Focused Configuration

# /etc/bws/performance.toml
[performance]
# Worker thread configuration
worker_threads = 16  # 2x CPU cores for I/O bound workloads
max_blocking_threads = 512

# Connection settings
max_connections = 100000
keep_alive_timeout = 60
request_timeout = 30
response_timeout = 30

# Buffer sizes
read_buffer_size = "64KB"
write_buffer_size = "64KB"
max_request_size = "10MB"

# Connection pooling
connection_pool_size = 1000
connection_pool_idle_timeout = 300

[caching]
enabled = true
max_memory = "1GB"
ttl_default = 3600
ttl_static = 86400

[compression]
enabled = true
level = 6  # Balance between CPU and compression ratio
min_size = 1024  # Don't compress small files

[[sites]]
name = "high-performance"
hostname = "0.0.0.0"
port = 8080
static_dir = "/opt/bws/static"

# Optimized headers for caching
[sites.headers]
"Cache-Control" = "public, max-age=31536000, immutable"
"Vary" = "Accept-Encoding"
"X-Content-Type-Options" = "nosniff"

Memory Management

[memory]
# Garbage collection tuning
gc_threshold = 0.8  # Trigger GC at 80% memory usage
max_heap_size = "4GB"

# Buffer pool settings
buffer_pool_size = "512MB"
buffer_pool_max_buffers = 10000

# Static file caching
file_cache_size = "2GB"
file_cache_max_files = 100000

CPU Optimization

[cpu]
# Thread affinity (if supported)
pin_threads = true
cpu_affinity = [0, 1, 2, 3, 4, 5, 6, 7]  # Pin to specific cores

# Async runtime tuning
io_uring = true  # Use io_uring if available (Linux 5.1+)
event_loop_threads = 4

Load Testing and Benchmarking

Benchmarking Tools

wrk - HTTP Benchmarking

# Install wrk
sudo apt install wrk

# Basic load test
wrk -t12 -c400 -d30s --latency http://localhost:8080/

# Custom script for complex scenarios
cat > test-script.lua << 'EOF'
wrk.method = "GET"
wrk.headers["User-Agent"] = "wrk-benchmark"

request = function()
    local path = "/static/file" .. math.random(1, 1000) .. ".jpg"
    return wrk.format(nil, path)
end
EOF

wrk -t12 -c400 -d30s -s test-script.lua http://localhost:8080/

Apache Bench (ab)

# Simple test
ab -n 10000 -c 100 http://localhost:8080/

# With keep-alive
ab -n 10000 -c 100 -k http://localhost:8080/

# POST requests
ab -n 1000 -c 10 -p post_data.json -T application/json http://localhost:8080/api/test

hey - Modern Load Testing

# Install hey
go install github.com/rakyll/hey@latest

# Basic test
hey -n 10000 -c 100 http://localhost:8080/

# With custom headers
hey -n 10000 -c 100 -H "Accept: application/json" http://localhost:8080/api/health

# Rate limited test
hey -n 10000 -q 100 http://localhost:8080/

Performance Testing Strategy

Baseline Testing

#!/bin/bash
# performance-baseline.sh

SERVER_URL="http://localhost:8080"
RESULTS_DIR="/tmp/performance-results"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$RESULTS_DIR"

echo "Running BWS performance baseline tests - $DATE"

# Test 1: Static file serving
echo "Testing static file serving..."
wrk -t4 -c50 -d60s --latency "$SERVER_URL/static/test.html" > "$RESULTS_DIR/static-$DATE.log"

# Test 2: API endpoints
echo "Testing API endpoints..."
wrk -t4 -c50 -d60s --latency "$SERVER_URL/health" > "$RESULTS_DIR/api-$DATE.log"

# Test 3: High concurrency
echo "Testing high concurrency..."
wrk -t12 -c1000 -d60s --latency "$SERVER_URL/" > "$RESULTS_DIR/concurrency-$DATE.log"

# Test 4: Sustained load
echo "Testing sustained load..."
wrk -t8 -c200 -d300s --latency "$SERVER_URL/" > "$RESULTS_DIR/sustained-$DATE.log"

echo "Performance tests completed. Results in $RESULTS_DIR"

Stress Testing

#!/bin/bash
# stress-test.sh

# Gradually increase load
for connections in 100 500 1000 2000 5000; do
    echo "Testing with $connections connections..."
    wrk -t12 -c$connections -d30s --latency http://localhost:8080/ > "stress-$connections.log"
    
    # Check if server is still responsive
    if ! curl -f -s http://localhost:8080/health > /dev/null; then
        echo "Server failed at $connections connections"
        break
    fi
    
    # Cool down period
    sleep 10
done

Performance Monitoring

Real-time Monitoring Script

#!/bin/bash
# monitor-performance.sh

LOG_FILE="/var/log/bws/performance.log"
INTERVAL=5

log_metrics() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
    local memory_usage=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}')
    local connections=$(ss -tuln | grep :8080 | wc -l)
    local load_avg=$(uptime | awk -F'load average:' '{print $2}' | cut -d',' -f1 | xargs)
    
    echo "$timestamp,CPU:$cpu_usage%,Memory:$memory_usage%,Connections:$connections,Load:$load_avg" >> "$LOG_FILE"
}

echo "Starting performance monitoring (interval: ${INTERVAL}s)"
echo "Timestamp,CPU,Memory,Connections,LoadAvg" > "$LOG_FILE"

while true; do
    log_metrics
    sleep $INTERVAL
done

Performance Dashboard Script

#!/bin/bash
# performance-dashboard.sh

# Real-time performance dashboard
watch -n 1 '
echo "=== BWS Performance Dashboard ==="
echo "Time: $(date)"
echo ""
echo "=== System Resources ==="
echo "CPU Usage: $(top -bn1 | grep "Cpu(s)" | awk "{print \$2}")"
echo "Memory: $(free -h | grep Mem | awk "{printf \"Used: %s/%s (%.1f%%)\", \$3, \$2, \$3/\$2*100}")"
echo "Load Average: $(uptime | awk -F"load average:" "{print \$2}")"
echo ""
echo "=== Network ==="
echo "Active Connections: $(ss -tuln | grep :8080 | wc -l)"
echo "TCP Connections: $(ss -s | grep TCP | head -1)"
echo ""
echo "=== BWS Process ==="
BWS_PID=$(pgrep bws)
if [ ! -z "$BWS_PID" ]; then
    echo "Process ID: $BWS_PID"
    echo "Memory Usage: $(ps -o pid,ppid,cmd,%mem,%cpu -p $BWS_PID | tail -1)"
    echo "File Descriptors: $(lsof -p $BWS_PID | wc -l)"
else
    echo "BWS process not found"
fi
'

Optimization Strategies

Static File Optimization

File Compression

#!/bin/bash
# precompress-static-files.sh

STATIC_DIR="/opt/bws/static"

# Pre-compress static files
find "$STATIC_DIR" -type f \( -name "*.css" -o -name "*.js" -o -name "*.html" -o -name "*.svg" \) | while read file; do
    # Gzip compression
    if [ ! -f "$file.gz" ] || [ "$file" -nt "$file.gz" ]; then
        gzip -k -9 "$file"
        echo "Compressed: $file"
    fi
    
    # Brotli compression (if available)
    if command -v brotli > /dev/null; then
        if [ ! -f "$file.br" ] || [ "$file" -nt "$file.br" ]; then
            brotli -k -q 11 "$file"
            echo "Brotli compressed: $file"
        fi
    fi
done

# Optimize images
find "$STATIC_DIR" -name "*.jpg" -o -name "*.jpeg" | while read file; do
    if command -v jpegoptim > /dev/null; then
        jpegoptim --strip-all --max=85 "$file"
    fi
done

find "$STATIC_DIR" -name "*.png" | while read file; do
    if command -v optipng > /dev/null; then
        optipng -o2 "$file"
    fi
done

CDN Integration

# Configure BWS for CDN usage
[[sites]]
name = "cdn-optimized"
hostname = "localhost"
port = 8080
static_dir = "/opt/bws/static"

[sites.headers]
"Cache-Control" = "public, max-age=31536000, immutable"
"Access-Control-Allow-Origin" = "*"
"Vary" = "Accept-Encoding"
"X-CDN-Cache" = "MISS"

# Separate configuration for CDN edge
[[sites]]
name = "edge"
hostname = "edge.example.com"
port = 8081
static_dir = "/opt/bws/edge-cache"

[sites.headers]
"Cache-Control" = "public, max-age=604800"  # 1 week for edge cache
"X-CDN-Cache" = "HIT"

Database and Cache Optimization

Redis Integration (if using caching)

# Redis performance tuning
# /etc/redis/redis.conf

# Memory settings
maxmemory 2gb
maxmemory-policy allkeys-lru

# Persistence settings (adjust based on needs)
save 900 1
save 300 10
save 60 10000

# Network settings
tcp-keepalive 60
timeout 0

# Performance settings
tcp-backlog 511
databases 16

Memcached Configuration

# /etc/memcached.conf
-m 1024  # 1GB memory
-c 1024  # Max connections
-t 4     # Number of threads
-l 127.0.0.1  # Listen address
-p 11211 # Port

Reverse Proxy Optimization

Nginx Performance Configuration

# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
worker_rlimit_nofile 100000;

events {
    worker_connections 4000;
    use epoll;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 30;
    keepalive_requests 100000;
    reset_timedout_connection on;
    client_body_timeout 10;
    send_timeout 2;
    
    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;
    
    # Buffer sizes
    client_max_body_size 10m;
    client_body_buffer_size 128k;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 4k;
    output_buffers 1 32k;
    postpone_output 1460;
    
    # Proxy settings
    proxy_buffering on;
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;
    
    upstream bws_backend {
        least_conn;
        server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
        server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
        keepalive 300;
    }
    
    server {
        listen 80 default_server;
        
        location / {
            proxy_pass http://bws_backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # Caching
            proxy_cache_valid 200 1h;
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        }
    }
}

Performance Monitoring and Analysis

Key Performance Metrics

Response Time Monitoring

#!/bin/bash
# response-time-monitor.sh

URL="http://localhost:8080"
LOG_FILE="/var/log/bws/response-times.log"

while true; do
    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    RESPONSE_TIME=$(curl -o /dev/null -s -w "%{time_total}" "$URL/health")
    RESPONSE_CODE=$(curl -o /dev/null -s -w "%{http_code}" "$URL/health")
    
    echo "$TIMESTAMP,$RESPONSE_TIME,$RESPONSE_CODE" >> "$LOG_FILE"
    sleep 1
done

Throughput Measurement

#!/bin/bash
# throughput-monitor.sh

# Monitor requests per second
LOG_FILE="/var/log/bws/throughput.log"
ACCESS_LOG="/var/log/nginx/access.log"

while true; do
    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    RPS=$(tail -60 "$ACCESS_LOG" | grep "$(date '+%d/%b/%Y:%H:%M')" | wc -l)
    
    echo "$TIMESTAMP,$RPS" >> "$LOG_FILE"
    sleep 60
done

Performance Analysis Tools

Log Analysis

#!/bin/bash
# analyze-performance.sh

LOG_FILE="/var/log/nginx/access.log"
OUTPUT_DIR="/tmp/performance-analysis"
DATE=$(date +%Y%m%d)

mkdir -p "$OUTPUT_DIR"

echo "Analyzing performance for $DATE"

# Top requested URLs
echo "=== Top Requested URLs ===" > "$OUTPUT_DIR/top-urls-$DATE.txt"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -20 >> "$OUTPUT_DIR/top-urls-$DATE.txt"

# Response codes distribution
echo "=== Response Codes ===" > "$OUTPUT_DIR/response-codes-$DATE.txt"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn >> "$OUTPUT_DIR/response-codes-$DATE.txt"

# Response time analysis (if logged)
echo "=== Response Time Analysis ===" > "$OUTPUT_DIR/response-times-$DATE.txt"
awk '{print $NF}' "$LOG_FILE" | grep -E '^[0-9]+\.[0-9]+$' | \
awk '{
    sum += $1
    count++
    if ($1 > max) max = $1
    if (min == 0 || $1 < min) min = $1
}
END {
    print "Average:", sum/count
    print "Min:", min
    print "Max:", max
}' >> "$OUTPUT_DIR/response-times-$DATE.txt"

# Hourly request distribution
echo "=== Hourly Request Distribution ===" > "$OUTPUT_DIR/hourly-requests-$DATE.txt"
awk '{print substr($4, 14, 2)}' "$LOG_FILE" | sort | uniq -c >> "$OUTPUT_DIR/hourly-requests-$DATE.txt"

echo "Analysis complete. Results in $OUTPUT_DIR"
#!/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

  1. Right-size worker threads: 2x CPU cores for I/O-bound workloads
  2. Optimize buffer sizes: Match your typical request/response sizes
  3. Enable compression: For text-based content
  4. Use connection pooling: Reuse connections for better performance
  5. Configure appropriate timeouts: Balance responsiveness and resource usage

Monitoring Best Practices

  1. Monitor key metrics: Response time, throughput, error rate, resource usage
  2. Set up alerting: For performance degradation
  3. Regular performance testing: Catch regressions early
  4. Capacity planning: Monitor trends and plan for growth
  5. Document baselines: Know your normal performance characteristics

Deployment Best Practices

  1. Load testing: Test under realistic load before production
  2. Gradual rollouts: Use blue-green or canary deployments
  3. Performance regression testing: Automated checks for performance changes
  4. Resource monitoring: Continuous monitoring of system resources
  5. Regular optimization: Periodic performance reviews and tuning

Next Steps

REST API

BWS provides a comprehensive REST API for monitoring, management, and configuration validation.

Endpoints

Health Check

GET /api/health

Returns the basic server health status.

Response:

{
  "status": "healthy",
  "timestamp": "2025-08-26T12:00:00Z",
  "version": "0.3.4",
  "uptime_seconds": 3600
}

Example:

curl http://localhost:8080/api/health

Detailed Health Information

GET /api/health/detailed

Returns comprehensive server information including system details.

Response:

{
  "status": "healthy",
  "timestamp": "2025-08-26T12:00:00Z",
  "version": "0.3.4",
  "uptime_seconds": 3600,
  "system": {
    "platform": "linux",
    "memory_usage": "45MB",
    "cpu_usage": "2.1%"
  },
  "sites": {
    "total": 4,
    "active": 4
  },
  "configuration": {
    "last_reload": "2025-08-26T11:30:00Z",
    "config_file": "/app/config.toml"
  }
}

Example:

curl http://localhost:8080/api/health/detailed | jq

Sites Information

GET /api/sites

Returns information about all configured sites.

Response:

{
  "sites": [
    {
      "name": "main",
      "hostname": "localhost",
      "port": 8080,
      "static_dir": "static",
      "default": true,
      "headers": {
        "X-Site-Name": "BWS Main Site",
        "X-Powered-By": "BWS/1.0"
      }
    }
  ],
  "total_sites": 1
}

Example:

curl http://localhost:8080/api/sites | jq

Configuration Reload

POST /api/reload

Triggers a hot reload of the configuration file without restarting the server.

Request:

curl -X POST http://localhost:8080/api/reload

Success Response (200 OK):

{
  "status": "success",
  "message": "Configuration reloaded successfully",
  "timestamp": "2025-08-26T12:00:00Z",
  "sites_reloaded": 4
}

Error Response (400 Bad Request):

{
  "status": "error",
  "message": "Configuration validation failed: Missing required field 'hostname' for site 'main'",
  "timestamp": "2025-08-26T12:00:00Z"
}

Note: The reload endpoint validates the new configuration before applying it. If validation fails, the existing configuration remains active.

Response Headers

All API responses include these headers:

  • Content-Type: application/json
  • X-Powered-By: BWS/0.3.4
  • Site-specific custom headers (if configured)

Error Responses

API endpoints return standard HTTP status codes:

  • 200 OK - Successful request
  • 400 Bad Request - Invalid request or configuration error
  • 404 Not Found - Endpoint not found
  • 500 Internal Server Error - Server error

Error Format:

{
  "error": "Not Found",
  "message": "The requested endpoint does not exist",
  "available_endpoints": ["/", "/api/health", "/api/sites"]
}

Management API

BWS provides a separate, secure Management API for administrative operations. This API runs on a dedicated port (default: 7654) and is restricted to localhost access only.

Security

The Management API implements security-first design:

  • Localhost Only: Always binds to 127.0.0.1 - no external access
  • IP Validation: Double-checks request origin is localhost
  • Optional API Key: Additional authentication layer
  • Audit Logging: All operations logged with client IP

Base URL

The Management API runs on a separate port from the main web server:

http://127.0.0.1:7654

Authentication

Authentication is optional but recommended for production:

# Without API key (localhost only)
curl -X POST http://127.0.0.1:7654/api/config/reload

# With API key
curl -X POST http://127.0.0.1:7654/api/config/reload \
  -H "X-API-Key: your-secure-api-key"

Configuration Reload

POST /api/config/reload

Reloads the server configuration from the configuration file.

Headers:

  • X-API-Key (optional): API key for authentication

Response (Success):

{
  "status": "success",
  "message": "Configuration reloaded successfully",
  "config_path": "config.toml",
  "timestamp": "2025-08-26T15:27:07Z",
  "note": "Changes will apply to new connections"
}

Response (Error):

{
  "error": "Configuration reload failed",
  "details": "Invalid TOML syntax at line 15"
}

Example:

# Basic reload
curl -X POST http://127.0.0.1:7654/api/config/reload

# With API key
curl -X POST http://127.0.0.1:7654/api/config/reload \
  -H "X-API-Key: your-secure-api-key"

Management API Errors

The Management API returns specific error codes:

  • 401 Unauthorized - Invalid or missing API key
  • 403 Forbidden - Request not from localhost
  • 404 Not Found - Endpoint does not exist
  • 500 Internal Server Error - Configuration reload failed

Security Error Example:

{
  "error": "Access denied: localhost only"
}

Authentication Error Example:

{
  "error": "Unauthorized: invalid API key"
}

Configuration Schema

This document provides a complete reference for all BWS configuration options in the config.toml file.

Configuration File Structure

BWS uses TOML format for configuration. The basic structure is:

# Global settings
[daemon]
# Daemon configuration

[logging]
# Logging configuration

[performance]
# Performance tuning

[monitoring]
# Health monitoring

# Site definitions
[[sites]]
# First site configuration

[[sites]]
# Second site configuration

Global Configuration Sections

Daemon Configuration

Controls how BWS runs as a daemon process.

[daemon]
user = "bws"                           # User to run as (string)
group = "bws"                          # Group to run as (string)
pid_file = "/var/run/bws.pid"          # PID file location (string)
working_directory = "/opt/bws"         # Working directory (string)
daemonize = true                       # Run as daemon (boolean)

Parameters:

  • user (string, optional): System user to run BWS as. Default: current user
  • group (string, optional): System group to run BWS as. Default: current user's group
  • pid_file (string, optional): Path to store process ID file. Default: no PID file
  • working_directory (string, optional): Change to this directory on startup
  • daemonize (boolean, optional): Fork and run in background. Default: false

Management API Configuration

Controls the secure Management API for administrative operations.

[management]
enabled = true                         # Enable Management API (boolean)
host = "127.0.0.1"                    # Host to bind to (string)
port = 7654                           # Port number (integer)
api_key = "your-secure-api-key"       # API key for authentication (string)

Parameters:

  • enabled (boolean, optional): Enable the Management API service. Default: false
  • host (string, optional): Host address to bind to. Always 127.0.0.1 for security. Default: "127.0.0.1"
  • port (integer, optional): Port number for the Management API. Default: 7654
  • api_key (string, optional): API key for authentication. If not set, no authentication required. Default: null

Security Features:

  • Management API always binds to localhost only for security
  • IP address validation ensures requests come from localhost
  • Optional API key authentication for additional security
  • All management operations are logged with client IP

Available Endpoints:

  • POST /api/config/reload: Reload server configuration

Logging Configuration

Controls logging behavior and output.

[logging]
level = "info"                         # Log level (string)
output = "stdout"                      # Output destination (string)
format = "json"                        # Log format (string)
file_path = "/var/log/bws/bws.log"    # Log file path (string)
max_size = "100MB"                     # Maximum log file size (string)
max_files = 10                         # Number of log files to keep (integer)
compress = true                        # Compress rotated logs (boolean)
include_fields = [                     # Fields to include in logs (array)
    "timestamp",
    "level", 
    "message",
    "request_id"
]

Parameters:

  • level (string, optional): Log level. Values: trace, debug, info, warn, error. Default: 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 file
  • max_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 cores
  • max_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 site
  • hostname (string): Hostname or IP address to bind to
  • port (integer): TCP port number to listen on
  • static_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 file
  • key_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 registration
  • staging (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 limiting
  • blacklist (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 access
  • deny_ips (array, optional): IP addresses or CIDR blocks denied access
  • require_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 - bytes
  • KB - kilobytes (1024 bytes)
  • MB - megabytes (1024 KB)
  • GB - gigabytes (1024 MB)

Examples: "1MB", "512KB", "2GB"

Duration Values

Duration values are integers representing seconds unless otherwise specified.

Arrays

Arrays use square brackets: ["item1", "item2"]

IP Addresses and CIDR

  • IPv4: "192.168.1.1"
  • IPv6: "2001:db8::1"
  • CIDR notation: "192.168.0.0/16", "10.0.0.0/8"

Configuration Validation

BWS validates configuration on startup. Common validation errors:

Syntax Errors

# Invalid: missing quotes
name = value  # Should be name = "value"

# Invalid: missing comma in array
ports = [8080 8081]  # Should be ports = [8080, 8081]

Type Errors

# Invalid: string instead of integer
port = "8080"  # Should be port = 8080

# Invalid: integer instead of boolean
enabled = 1  # Should be enabled = true

Logical Errors

# Invalid: duplicate site names
[[sites]]
name = "main"

[[sites]]
name = "main"  # Error: duplicate name

# Invalid: missing required fields
[[sites]]
hostname = "localhost"  # Error: missing port and static_dir

Environment Variable Overrides

Some configuration values can be overridden with environment variables:

BWS_CONFIG=/path/to/config.toml       # Configuration file path
BWS_LOG_FILE=/path/to/log/file        # Override logging.file_path
BWS_PID_FILE=/path/to/pid/file        # Override daemon.pid_file
BWS_STATIC_DIR=/path/to/static        # Override sites.static_dir (first site)
BWS_PORT=8080                         # Override sites.port (first site)
BWS_HOSTNAME=localhost                # Override sites.hostname (first site)
RUST_LOG=debug                        # Override logging.level

Configuration Best Practices

Security

  • Set appropriate file permissions (600) for configuration files
  • Use separate configuration files for different environments
  • Store sensitive data in environment variables or external secret management
  • Regularly review and audit configuration changes

Performance

  • Tune worker threads based on workload characteristics
  • Configure appropriate timeouts for your use case
  • Enable compression for text-based content
  • Use caching for frequently accessed static files

Monitoring

  • Enable health endpoints for monitoring
  • Configure appropriate thresholds for alerts
  • Use structured logging for better analysis
  • Monitor resource usage trends

Maintenance

  • Use version control for configuration files
  • Document configuration changes
  • Test configuration changes in non-production environments
  • Implement configuration validation in CI/CD pipelines

Example Configurations

BWS includes several example configurations for different use cases:

Test Configurations

# Virtual hosting test (multiple sites on same port)
tests/test_multisite_shared_port.toml

# Multi-site test (different ports)
tests/config_test.toml

# Load balancing test
tests/test_load_balancing.toml

# WebSocket proxy test
tests/test_websocket_proxy.toml

Testing Virtual Hosting

The virtual hosting test demonstrates multiple sites sharing port 8080:

# Run the virtual hosting test
./tests/test_multisite_shared_port.sh test

# View the configuration
cat tests/test_multisite_shared_port.toml

This test includes:

  • 4 virtual hosts: www.local.com, blog.local.com, api.local.com, dev.local.com
  • Shared port: All sites use port 8080
  • Host-based routing: Routes based on HTTP Host header
  • Site-specific content: Each site serves different static files
  • Custom headers: Site-specific response headers

Development Configurations

# Basic single site
examples/basic-single-site.toml

# Multi-site setup
examples/basic-multi-site.toml

# SSL with ACME
examples/ssl-acme.toml

# Production setup
examples/production-multi-site.toml

Configuration Templates

Create your own configuration based on these templates:

# Copy a template
cp tests/test_multisite_shared_port.toml my-config.toml

# Customize for your needs
# - Update hostnames to your domains
# - Change static_dir paths
# - Configure SSL settings
# - Add your custom headers

# Test the configuration
./target/release/bws --config my-config.toml

Next Steps

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

  1. 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
    
  2. 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
    
  3. 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 feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting, etc.)
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: 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

  1. Automated Checks: CI/CD pipeline runs automatically
  2. Code Review: Maintainers review the changes
  3. Feedback: Address any requested changes
  4. 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:

  1. Functionality: Does the code work correctly?
  2. Style: Does it follow project conventions?
  3. Performance: Are there performance implications?
  4. Security: Are there security concerns?
  5. Maintainability: Is the code easy to understand and maintain?
  6. 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

# 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

  1. Update Version Numbers

    # Update Cargo.toml
    version = "0.2.0"
    
    # Update documentation references
    
  2. Update CHANGELOG.md

    ## [0.2.0] - 2024-01-15
    
    ### Added
    - New feature descriptions
    
    ### Changed
    - Changed feature descriptions
    
    ### Fixed
    - Bug fix descriptions
    
  3. Run Full Test Suite

    cargo test --all-features
    cargo clippy --all-targets --all-features
    cargo fmt --check
    cargo audit
    
  4. Update Documentation

    cargo doc --no-deps
    mdbook build docs/
    
  5. 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:

  1. Check existing documentation
  2. Search GitHub issues
  3. Ask in GitHub Discussions
  4. 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

  1. Create feature branch
  2. Add configuration options
  3. Implement core functionality
  4. Add comprehensive tests
  5. Update documentation
  6. Submit pull request

Performance Optimization

When optimizing performance:

  1. Measure First: Use benchmarks to identify bottlenecks
  2. Profile: Use profiling tools to understand behavior
  3. Optimize: Make targeted improvements
  4. Verify: Confirm improvements with benchmarks
  5. 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

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 export
  • tls: Enable TLS/SSL support
  • jemalloc: Use jemalloc allocator for better performance
  • mimalloc: 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;
# 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 of cargo 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:

  1. Read the Configuration Guide to set up your server
  2. Follow the Quick Start to get running
  3. Check Performance Tuning for optimization
  4. Review Production Setup for deployment

For development:

  1. Read the Contributing Guide
  2. Set up your development environment
  3. Run the test suite
  4. Start contributing!

Testing

This guide covers testing strategies, tools, and best practices for BWS development and deployment.

Testing Overview

BWS includes multiple layers of testing:

  • Unit Tests: Test individual functions and modules using Rust's built-in test framework
  • Integration Tests: Test component interactions and API endpoints
  • Configuration Tests: Validate configuration files using --dry-run
  • End-to-End Tests: Test complete workflows including hot reload and multi-site functionality
  • Performance Tests: Measure performance characteristics under load
  • Security Tests: Validate security measures and headers

Configuration Validation Testing

BWS includes comprehensive configuration validation that can be used for testing:

Using --dry-run for Testing

# Validate configuration files without starting server
bws --config config.toml --dry-run

# Test all example configurations
for config in examples/*.toml; do
  echo "Testing $config..."
  bws --config "$config" --dry-run
done

# Test production configurations
bws --config production.toml --dry-run

Automated Configuration Testing

BWS includes automated test scripts for comprehensive configuration validation:

# Run comprehensive configuration validation
./tests/scripts/validate-configs.sh

# Test specific configuration categories
./tests/scripts/validate-configs.sh --examples-only
./tests/scripts/validate-configs.sh --tests-only

Running Tests

Basic Test Commands

# Run all Rust unit tests
cargo test

# Run tests with output
cargo test -- --nocapture

# Run specific test
cargo test test_config_parsing

# Run tests matching pattern
cargo test config

# Run ignored tests
cargo test -- --ignored

# Run tests in single thread (for debugging)
cargo test -- --test-threads=1

Integration Test Scripts

BWS includes organized test scripts for comprehensive testing:

# Configuration validation tests
./tests/scripts/validate-configs.sh

# HTTP header functionality tests  
./tests/scripts/test_headers.sh

# Multi-site hosting tests
./tests/scripts/test_multisite.sh

# Load balancing tests
./tests/scripts/test_load_balance.sh

# WebSocket proxy tests
./tests/scripts/simple_websocket_test.sh

# Hot reload functionality tests
./tests/scripts/test_hot_reload.sh

# Static file serving tests
./tests/scripts/test_static_server.sh

Test Categories

# Run only unit tests
cargo test --lib

# Run only integration tests
cargo test --test integration

# Run only documentation tests
cargo test --doc

# Run tests for specific package
cargo test -p bws-core

Test with Features

# Test with all features
cargo test --all-features

# Test with specific features
cargo test --features "compression,metrics"

# Test without default features
cargo test --no-default-features

Unit Testing

Basic Unit Tests

#![allow(unused)]
fn main() {
// src/config.rs
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_default() {
        let config = Config::default();
        assert_eq!(config.sites.len(), 0);
    }

    #[test]
    fn test_config_parsing() {
        let toml_str = r#"
            [[sites]]
            name = "test"
            hostname = "localhost"
            port = 8080
            static_dir = "static"
        "#;

        let config: Config = toml::from_str(toml_str).unwrap();
        assert_eq!(config.sites.len(), 1);
        assert_eq!(config.sites[0].name, "test");
        assert_eq!(config.sites[0].port, 8080);
    }

    #[test]
    #[should_panic(expected = "Invalid port")]
    fn test_invalid_port() {
        let site = Site {
            name: "test".to_string(),
            hostname: "localhost".to_string(),
            port: 0, // Invalid port
            static_dir: "static".to_string(),
            ..Default::default()
        };
        site.validate().unwrap();
    }
}
}

Testing Error Conditions

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;
    use anyhow::Result;

    #[test]
    fn test_file_not_found() {
        let result = read_config_file("nonexistent.toml");
        assert!(result.is_err());
        
        let error = result.unwrap_err();
        assert!(error.to_string().contains("No such file"));
    }

    #[test]
    fn test_invalid_toml() {
        let invalid_toml = "invalid toml content [[[";
        let result = parse_config(invalid_toml);
        assert!(result.is_err());
    }

    #[test]
    fn test_missing_required_field() {
        let toml_str = r#"
            [[sites]]
            name = "test"
            Missing hostname, port, static_dir
        "#;
        
        let result: Result<Config, _> = toml::from_str(toml_str);
        assert!(result.is_err());
    }
}
}

Mocking and Test Doubles

#![allow(unused)]
fn main() {
// Use mockall for mocking
use mockall::predicate::*;
use mockall::mock;

mock! {
    FileSystem {
        fn read_file(&self, path: &str) -> Result<String>;
        fn file_exists(&self, path: &str) -> bool;
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_loading_with_mock() {
        let mut mock_fs = MockFileSystem::new();
        mock_fs
            .expect_read_file()
            .with(eq("config.toml"))
            .times(1)
            .returning(|_| Ok(r#"
                [[sites]]
                name = "test"
                hostname = "localhost"
                port = 8080
                static_dir = "static"
            "#.to_string()));

        let config = load_config_with_fs(&mock_fs, "config.toml").unwrap();
        assert_eq!(config.sites.len(), 1);
    }
}
}

Integration Testing

Test Structure

#![allow(unused)]
fn main() {
// tests/integration/server_tests.rs
use bws::{Config, Server};
use std::time::Duration;
use tokio::time::sleep;

#[tokio::test]
async fn test_server_startup_shutdown() {
    let config = test_config();
    let server = Server::new(config).await.unwrap();
    
    // Start server in background
    let handle = tokio::spawn(async move {
        server.run().await
    });
    
    // Give server time to start
    sleep(Duration::from_millis(100)).await;
    
    // Test server is responding
    let response = reqwest::get("http://127.0.0.1:8080/health").await.unwrap();
    assert_eq!(response.status(), 200);
    
    // Shutdown server
    handle.abort();
}

#[tokio::test]
async fn test_static_file_serving() {
    let temp_dir = setup_test_static_files().await;
    let config = Config {
        sites: vec![Site {
            name: "test".to_string(),
            hostname: "127.0.0.1".to_string(),
            port: 8081,
            static_dir: temp_dir.path().to_string_lossy().to_string(),
            ..Default::default()
        }],
        ..Default::default()
    };
    
    let server = Server::new(config).await.unwrap();
    let handle = tokio::spawn(async move {
        server.run().await
    });
    
    sleep(Duration::from_millis(100)).await;
    
    // Test serving static file
    let response = reqwest::get("http://127.0.0.1:8081/test.html").await.unwrap();
    assert_eq!(response.status(), 200);
    assert_eq!(response.text().await.unwrap(), "<h1>Test</h1>");
    
    handle.abort();
    cleanup_test_files(temp_dir).await;
}

fn test_config() -> Config {
    Config {
        sites: vec![Site {
            name: "test".to_string(),
            hostname: "127.0.0.1".to_string(),
            port: 8080,
            static_dir: "test_static".to_string(),
            ..Default::default()
        }],
        ..Default::default()
    }
}

async fn setup_test_static_files() -> tempfile::TempDir {
    let temp_dir = tempfile::tempdir().unwrap();
    
    tokio::fs::write(
        temp_dir.path().join("test.html"),
        "<h1>Test</h1>"
    ).await.unwrap();
    
    tokio::fs::write(
        temp_dir.path().join("index.html"),
        "<h1>Index</h1>"
    ).await.unwrap();
    
    temp_dir
}
}

HTTP Client Testing

#![allow(unused)]
fn main() {
// tests/integration/http_tests.rs
use reqwest::Client;
use serde_json::Value;

#[tokio::test]
async fn test_health_endpoint() {
    let client = Client::new();
    let response = client
        .get("http://127.0.0.1:8080/health")
        .send()
        .await
        .unwrap();
    
    assert_eq!(response.status(), 200);
    assert_eq!(response.headers().get("content-type").unwrap(), "application/json");
    
    let body: Value = response.json().await.unwrap();
    assert_eq!(body["status"], "healthy");
}

#[tokio::test]
async fn test_custom_headers() {
    let client = Client::new();
    let response = client
        .get("http://127.0.0.1:8080/")
        .send()
        .await
        .unwrap();
    
    // Check custom headers are present
    assert!(response.headers().contains_key("x-served-by"));
    assert_eq!(response.headers()["cache-control"], "public, max-age=3600");
}

#[tokio::test]
async fn test_cors_headers() {
    let client = Client::new();
    let response = client
        .options("http://127.0.0.1:8080/")
        .header("Origin", "https://example.com")
        .header("Access-Control-Request-Method", "GET")
        .send()
        .await
        .unwrap();
    
    assert_eq!(response.status(), 200);
    assert!(response.headers().contains_key("access-control-allow-origin"));
}
}

Database Integration Tests

#![allow(unused)]
fn main() {
// tests/integration/database_tests.rs (if BWS had database features)
use sqlx::PgPool;

#[tokio::test]
async fn test_database_connection() {
    let pool = setup_test_database().await;
    
    let config = Config {
        database_url: Some(pool.connect_options().to_url_lossy().to_string()),
        ..test_config()
    };
    
    let server = Server::new(config).await.unwrap();
    
    // Test database-dependent endpoints
    let response = reqwest::get("http://127.0.0.1:8080/api/data").await.unwrap();
    assert_eq!(response.status(), 200);
    
    cleanup_test_database(pool).await;
}

async fn setup_test_database() -> PgPool {
    // Set up test database
    PgPool::connect("postgres://test:test@localhost/bws_test")
        .await
        .unwrap()
}
}

End-to-End Testing

Test Scenarios

#![allow(unused)]
fn main() {
// tests/e2e/scenarios.rs
use std::process::{Command, Stdio};
use std::time::Duration;
use tokio::time::sleep;

#[tokio::test]
async fn test_complete_deployment_scenario() {
    // 1. Create test configuration
    let config_content = r#"
        [daemon]
        pid_file = "/tmp/bws-test.pid"
        
        [logging]
        level = "info"
        output = "file"
        file_path = "/tmp/bws-test.log"
        
        [[sites]]
        name = "main"
        hostname = "127.0.0.1"
        port = 8080
        static_dir = "test_static"
        
        [sites.headers]
        "Cache-Control" = "public, max-age=3600"
    "#;
    
    std::fs::write("test-config.toml", config_content).unwrap();
    
    // 2. Create static files
    std::fs::create_dir_all("test_static").unwrap();
    std::fs::write("test_static/index.html", "<h1>Welcome to BWS</h1>").unwrap();
    std::fs::write("test_static/style.css", "body { color: blue; }").unwrap();
    
    // 3. Start BWS server
    let mut child = Command::new("target/release/bws")
        .arg("--config")
        .arg("test-config.toml")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .unwrap();
    
    // 4. Wait for server to start
    sleep(Duration::from_secs(2)).await;
    
    // 5. Run tests
    test_homepage().await;
    test_static_files().await;
    test_health_check().await;
    test_performance().await;
    
    // 6. Cleanup
    child.kill().unwrap();
    std::fs::remove_file("test-config.toml").unwrap();
    std::fs::remove_dir_all("test_static").unwrap();
    std::fs::remove_file("/tmp/bws-test.log").ok();
    std::fs::remove_file("/tmp/bws-test.pid").ok();
}

async fn test_homepage() {
    let response = reqwest::get("http://127.0.0.1:8080/").await.unwrap();
    assert_eq!(response.status(), 200);
    assert!(response.text().await.unwrap().contains("Welcome to BWS"));
}

async fn test_static_files() {
    let response = reqwest::get("http://127.0.0.1:8080/style.css").await.unwrap();
    assert_eq!(response.status(), 200);
    assert_eq!(response.headers()["content-type"], "text/css");
    assert!(response.text().await.unwrap().contains("color: blue"));
}

async fn test_health_check() {
    let response = reqwest::get("http://127.0.0.1:8080/health").await.unwrap();
    assert_eq!(response.status(), 200);
    
    let health: serde_json::Value = response.json().await.unwrap();
    assert_eq!(health["status"], "healthy");
}

async fn test_performance() {
    use std::time::Instant;
    
    let start = Instant::now();
    
    // Make 100 concurrent requests
    let futures: Vec<_> = (0..100)
        .map(|_| reqwest::get("http://127.0.0.1:8080/"))
        .collect();
    
    let responses = futures::future::join_all(futures).await;
    let duration = start.elapsed();
    
    // All requests should succeed
    for response in responses {
        assert_eq!(response.unwrap().status(), 200);
    }
    
    // Should complete in reasonable time
    assert!(duration < Duration::from_secs(5));
    println!("100 requests completed in {:?}", duration);
}
}

Multi-Site Testing

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_multi_site_configuration() {
    let config_content = r#"
        [[sites]]
        name = "main"
        hostname = "127.0.0.1"
        port = 8080
        static_dir = "main_static"
        
        [[sites]]
        name = "api"
        hostname = "127.0.0.1"
        port = 8081
        static_dir = "api_static"
        
        [sites.headers]
        "Content-Type" = "application/json"
    "#;
    
    // Setup and test both sites
    setup_multi_site_files();
    
    let mut child = start_bws_server("multi-site-config.toml");
    sleep(Duration::from_secs(2)).await;
    
    // Test main site
    let response = reqwest::get("http://127.0.0.1:8080/").await.unwrap();
    assert_eq!(response.status(), 200);
    
    // Test API site
    let response = reqwest::get("http://127.0.0.1:8081/").await.unwrap();
    assert_eq!(response.status(), 200);
    assert_eq!(response.headers()["content-type"], "application/json");
    
    cleanup_multi_site_test(child);
}
}

Virtual Hosting (Shared Port) Testing

BWS supports virtual hosting where multiple sites share the same port but are distinguished by hostname. This is particularly useful for hosting multiple domains on standard ports (80/443).

Test Configuration

# tests/test_multisite_shared_port.toml
[server]
name = "BWS Multi-Site Shared Port Test Server"

# All sites share port 8080 but have different hostnames
[[sites]]
name = "main"
hostname = "www.local.com"
port = 8080
static_dir = "examples/sites/static"
default = true

[sites.headers]
"X-Site-Name" = "Main Site"
"X-Port-Sharing" = "enabled"

[[sites]]
name = "blog"
hostname = "blog.local.com"
port = 8080
static_dir = "examples/sites/static-blog"

[sites.headers]
"X-Site-Name" = "Blog Site"
"X-Port-Sharing" = "enabled"

[[sites]]
name = "api"
hostname = "api.local.com"
port = 8080
static_dir = "examples/sites/static-api"

[sites.headers]
"X-Site-Name" = "API Documentation"
"X-Port-Sharing" = "enabled"

Running Virtual Hosting Tests

# Automated test script
./tests/test_multisite_shared_port.sh test

# Manual testing with Host headers
curl -H "Host: www.local.com" http://127.0.0.1:8080
curl -H "Host: blog.local.com" http://127.0.0.1:8080
curl -H "Host: api.local.com" http://127.0.0.1:8080

# Check site-specific headers
curl -I -H "Host: www.local.com" http://127.0.0.1:8080

Setting Up Local Testing

For browser testing, add domains to /etc/hosts:

sudo bash -c 'echo "127.0.0.1 www.local.com blog.local.com api.local.com dev.local.com" >> /etc/hosts'

Then access:

  • http://www.local.com:8080 (Main Site)
  • http://blog.local.com:8080 (Blog)
  • http://api.local.com:8080 (API Docs)
  • http://dev.local.com:8080 (Development)

Virtual Hosting Integration Test

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_virtual_hosting_shared_port() {
    setup_virtual_hosting_sites();
    
    let mut child = start_bws_server("test_multisite_shared_port.toml");
    sleep(Duration::from_secs(2)).await;
    
    // Test main site
    let client = reqwest::Client::new();
    let response = client
        .get("http://127.0.0.1:8080/")
        .header("Host", "www.local.com")
        .send()
        .await
        .unwrap();
    
    assert_eq!(response.status(), 200);
    assert_eq!(response.headers()["x-site-name"], "Main Site");
    assert_eq!(response.headers()["x-port-sharing"], "enabled");
    
    // Test blog site (same port, different hostname)
    let response = client
        .get("http://127.0.0.1:8080/")
        .header("Host", "blog.local.com")
        .send()
        .await
        .unwrap();
    
    assert_eq!(response.status(), 200);
    assert_eq!(response.headers()["x-site-name"], "Blog Site");
    assert_eq!(response.headers()["x-port-sharing"], "enabled");
    
    // Test API site
    let response = client
        .get("http://127.0.0.1:8080/")
        .header("Host", "api.local.com")
        .send()
        .await
        .unwrap();
    
    assert_eq!(response.status(), 200);
    assert_eq!(response.headers()["x-site-name"], "API Documentation");
    assert_eq!(response.headers()["x-port-sharing"], "enabled");
    
    cleanup_virtual_hosting_test(child);
}
}

WebSocket Proxy Testing

#![allow(unused)]
fn main() {
// tests/integration/websocket_tests.rs
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::test]
async fn test_websocket_proxy_configuration() {
    let config_content = r#"
        [[sites]]
        name = "websocket-proxy"
        hostname = "127.0.0.1"
        port = 8090
        static_dir = "static"
        
        [sites.proxy]
        enabled = true
        
        [[sites.proxy.upstreams]]
        name = "websocket_backend"
        url = "http://127.0.0.1:3001"
        weight = 1
        
        [[sites.proxy.upstreams]]
        name = "websocket_backend"
        url = "http://127.0.0.1:3002"
        weight = 1
        
        [[sites.proxy.routes]]
        path = "/ws"
        upstream = "websocket_backend"
        strip_prefix = true
        websocket = true
        
        [sites.proxy.load_balancing]
        method = "round_robin"
    "#;
    
    // Start mock WebSocket servers
    let server1 = start_mock_websocket_server(3001).await;
    let server2 = start_mock_websocket_server(3002).await;
    
    // Start BWS with WebSocket proxy config
    let bws_server = start_bws_server_with_config(config_content).await;
    
    // Test WebSocket upgrade detection
    test_websocket_upgrade_detection().await;
    
    // Test WebSocket proxy connection
    test_websocket_proxy_connection().await;
    
    // Test load balancing
    test_websocket_load_balancing().await;
    
    // Cleanup
    cleanup_websocket_test(bws_server, server1, server2).await;
}

async fn test_websocket_upgrade_detection() {
    // Test that BWS properly detects WebSocket upgrade requests
    let client = reqwest::Client::new();
    let response = client
        .get("http://127.0.0.1:8090/ws")
        .header("Upgrade", "websocket")
        .header("Connection", "Upgrade")
        .header("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
        .header("Sec-WebSocket-Version", "13")
        .send()
        .await
        .unwrap();
    
    // Should attempt WebSocket upgrade (not 404)
    assert_ne!(response.status(), 404);
}

async fn test_websocket_proxy_connection() {
    // Note: This test demonstrates the framework
    // Full implementation requires additional Pingora integration
    
    // Attempt WebSocket connection through proxy
    let ws_url = "ws://127.0.0.1:8090/ws";
    
    // In a complete implementation, this would succeed
    match connect_async(ws_url).await {
        Ok((mut ws_stream, _)) => {
            // Send test message
            ws_stream.send(Message::Text("test".to_string())).await.unwrap();
            
            // Receive response
            if let Some(msg) = ws_stream.next().await {
                let msg = msg.unwrap();
                assert!(msg.is_text());
                println!("Received: {}", msg.to_text().unwrap());
            }
        }
        Err(e) => {
            // Expected in current implementation
            println!("WebSocket connection failed (expected): {}", e);
        }
    }
}

async fn test_websocket_load_balancing() {
    // Test that WebSocket connections are load balanced
    let mut server_responses = std::collections::HashMap::new();
    
    // Make multiple connections
    for i in 0..10 {
        let ws_url = format!("ws://127.0.0.1:8090/ws?test={}", i);
        
        // In full implementation, track which server responds
        // Current implementation provides detection framework
        match connect_async(&ws_url).await {
            Ok((mut ws_stream, _)) => {
                ws_stream.send(Message::Text("ping".to_string())).await.unwrap();
                
                if let Some(msg) = ws_stream.next().await {
                    let response = msg.unwrap().to_text().unwrap();
                    *server_responses.entry(response.to_string()).or_insert(0) += 1;
                }
            }
            Err(_) => {
                // Expected in current framework implementation
            }
        }
    }
    
    // In full implementation, verify load balancing distribution
    println!("Server response distribution: {:?}", server_responses);
}

async fn start_mock_websocket_server(port: u16) -> tokio::task::JoinHandle<()> {
    tokio::spawn(async move {
        use tokio_tungstenite::{accept_async, tungstenite::Message};
        use tokio::net::{TcpListener, TcpStream};
        
        let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).await.unwrap();
        println!("Mock WebSocket server listening on port {}", port);
        
        while let Ok((stream, _)) = listener.accept().await {
            tokio::spawn(handle_websocket_connection(stream, port));
        }
    })
}

async fn handle_websocket_connection(stream: TcpStream, port: u16) {
    match accept_async(stream).await {
        Ok(mut websocket) => {
            while let Some(msg) = websocket.next().await {
                match msg {
                    Ok(Message::Text(text)) => {
                        let response = format!("Echo from server {}: {}", port, text);
                        websocket.send(Message::Text(response)).await.unwrap();
                    }
                    Ok(Message::Close(_)) => break,
                    Err(e) => {
                        println!("WebSocket error: {}", e);
                        break;
                    }
                    _ => {}
                }
            }
        }
        Err(e) => println!("WebSocket handshake failed: {}", e),
    }
}
}

WebSocket Test Script

#!/bin/bash
# Run the WebSocket proxy test script
./tests/test_websocket_proxy.sh

This script will:

  • Start multiple WebSocket test servers
  • Configure BWS with WebSocket proxy routes
  • Provide a web interface for manual testing
  • Demonstrate load balancing between upstream servers

Performance Testing

Load Testing with wrk

#!/bin/bash
# scripts/load-test.sh

BWS_PID=""

setup_test_server() {
    echo "Setting up test server..."
    
    # Create test configuration
    cat > test-load-config.toml << EOF
[[sites]]
name = "load-test"
hostname = "127.0.0.1"
port = 8080
static_dir = "load_test_static"

[sites.headers]
"Cache-Control" = "public, max-age=3600"
EOF

    # Create test files
    mkdir -p load_test_static
    echo "<h1>Load Test Page</h1>" > load_test_static/index.html
    
    # Generate test files of various sizes
    dd if=/dev/zero of=load_test_static/1kb.txt bs=1024 count=1 2>/dev/null
    dd if=/dev/zero of=load_test_static/10kb.txt bs=1024 count=10 2>/dev/null
    dd if=/dev/zero of=load_test_static/100kb.txt bs=1024 count=100 2>/dev/null
    
    # Start BWS
    ./target/release/bws --config test-load-config.toml &
    BWS_PID=$!
    
    sleep 2
}

run_load_tests() {
    echo "Running load tests..."
    
    # Test 1: Basic load test
    echo "=== Basic Load Test ==="
    wrk -t4 -c50 -d30s --latency http://127.0.0.1:8080/
    
    # Test 2: High concurrency
    echo "=== High Concurrency Test ==="
    wrk -t8 -c200 -d30s --latency http://127.0.0.1:8080/
    
    # Test 3: Different file sizes
    echo "=== 1KB File Test ==="
    wrk -t4 -c50 -d15s http://127.0.0.1:8080/1kb.txt
    
    echo "=== 10KB File Test ==="
    wrk -t4 -c50 -d15s http://127.0.0.1:8080/10kb.txt
    
    echo "=== 100KB File Test ==="
    wrk -t4 -c50 -d15s http://127.0.0.1:8080/100kb.txt
    
    # Test 4: Sustained load
    echo "=== Sustained Load Test (5 minutes) ==="
    wrk -t4 -c100 -d300s --latency http://127.0.0.1:8080/
}

cleanup_test() {
    echo "Cleaning up..."
    if [ ! -z "$BWS_PID" ]; then
        kill $BWS_PID 2>/dev/null
    fi
    rm -rf load_test_static test-load-config.toml
}

# Trap cleanup on script exit
trap cleanup_test EXIT

setup_test_server
run_load_tests

Benchmark Tests

#![allow(unused)]
fn main() {
// benches/server_benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use bws::{Config, Server};

fn bench_config_parsing(c: &mut Criterion) {
    let config_str = r#"
        [[sites]]
        name = "bench"
        hostname = "127.0.0.1"
        port = 8080
        static_dir = "static"
        
        [sites.headers]
        "Cache-Control" = "public, max-age=3600"
        "X-Content-Type-Options" = "nosniff"
    "#;
    
    c.bench_function("parse config", |b| {
        b.iter(|| {
            let _config: Config = toml::from_str(black_box(config_str)).unwrap();
        })
    });
}

fn bench_static_file_resolution(c: &mut Criterion) {
    let config = test_config();
    
    c.bench_function("resolve static file", |b| {
        b.iter(|| {
            let _path = resolve_static_file(
                black_box("/assets/css/main.css"),
                black_box(&config.sites[0])
            );
        })
    });
}

criterion_group!(benches, bench_config_parsing, bench_static_file_resolution);
criterion_main!(benches);
}

Memory Testing

#![allow(unused)]
fn main() {
// tests/memory_tests.rs
#[test]
fn test_memory_usage() {
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};
    
    let memory_counter = Arc::new(AtomicUsize::new(0));
    
    // Custom allocator to track memory usage
    #[global_allocator]
    static GLOBAL: TrackingAllocator = TrackingAllocator;
    
    let initial_memory = get_memory_usage();
    
    // Create large number of configs
    let configs: Vec<Config> = (0..1000)
        .map(|i| Config {
            sites: vec![Site {
                name: format!("site_{}", i),
                hostname: "127.0.0.1".to_string(),
                port: 8080 + i,
                static_dir: format!("static_{}", i),
                ..Default::default()
            }],
            ..Default::default()
        })
        .collect();
    
    let peak_memory = get_memory_usage();
    drop(configs);
    
    // Force garbage collection
    std::hint::black_box(());
    
    let final_memory = get_memory_usage();
    
    println!("Initial memory: {} KB", initial_memory / 1024);
    println!("Peak memory: {} KB", peak_memory / 1024);
    println!("Final memory: {} KB", final_memory / 1024);
    
    // Memory should be released
    assert!(final_memory < peak_memory);
}
}

Security Testing

Input Validation Tests

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_path_traversal_protection() {
    // Test various path traversal attempts
    let malicious_paths = vec![
        "../../../etc/passwd",
        "..%2F..%2F..%2Fetc%2Fpasswd",
        "....//....//....//etc//passwd",
        "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
    ];
    
    for path in malicious_paths {
        let response = reqwest::get(&format!("http://127.0.0.1:8080/{}", path))
            .await
            .unwrap();
        
        // Should return 404 or 403, not 200
        assert_ne!(response.status(), 200);
    }
}

#[tokio::test]
async fn test_request_size_limits() {
    let client = reqwest::Client::new();
    
    // Test large request body
    let large_body = "x".repeat(10 * 1024 * 1024); // 10MB
    
    let response = client
        .post("http://127.0.0.1:8080/")
        .body(large_body)
        .send()
        .await
        .unwrap();
    
    // Should reject large requests
    assert_eq!(response.status(), 413); // Payload Too Large
}

#[tokio::test]
async fn test_header_injection() {
    let client = reqwest::Client::new();
    
    // Test header injection attempts
    let response = client
        .get("http://127.0.0.1:8080/")
        .header("X-Forwarded-For", "malicious\r\nContent-Type: text/html")
        .send()
        .await
        .unwrap();
    
    // Response should not contain injected header
    assert!(!response.headers().contains_key("content-type"));
}
}

Rate Limiting Tests

#![allow(unused)]
fn main() {
#[tokio::test]
async fn test_rate_limiting() {
    let client = reqwest::Client::new();
    
    // Make requests rapidly
    let mut success_count = 0;
    let mut rate_limited_count = 0;
    
    for _ in 0..100 {
        let response = client
            .get("http://127.0.0.1:8080/")
            .send()
            .await
            .unwrap();
        
        match response.status().as_u16() {
            200 => success_count += 1,
            429 => rate_limited_count += 1, // Too Many Requests
            _ => {}
        }
    }
    
    // Should have some rate limited responses
    assert!(rate_limited_count > 0);
    println!("Success: {}, Rate Limited: {}", success_count, rate_limited_count);
}
}

Test Utilities and Helpers

Test Configuration Factory

#![allow(unused)]
fn main() {
// tests/common/mod.rs
pub fn test_config() -> Config {
    Config {
        daemon: DaemonConfig::default(),
        logging: LoggingConfig {
            level: "debug".to_string(),
            output: "stdout".to_string(),
            ..Default::default()
        },
        sites: vec![Site {
            name: "test".to_string(),
            hostname: "127.0.0.1".to_string(),
            port: 8080,
            static_dir: "test_static".to_string(),
            ..Default::default()
        }],
        ..Default::default()
    }
}

pub fn test_config_with_port(port: u16) -> Config {
    let mut config = test_config();
    config.sites[0].port = port;
    config
}

pub fn test_config_multi_site() -> Config {
    Config {
        sites: vec![
            Site {
                name: "main".to_string(),
                hostname: "127.0.0.1".to_string(),
                port: 8080,
                static_dir: "main_static".to_string(),
                ..Default::default()
            },
            Site {
                name: "api".to_string(),
                hostname: "127.0.0.1".to_string(),
                port: 8081,
                static_dir: "api_static".to_string(),
                ..Default::default()
            },
        ],
        ..Default::default()
    }
}
}

Test Server Management

#![allow(unused)]
fn main() {
use std::sync::Once;
use tokio::sync::Mutex;

static INIT: Once = Once::new();
static TEST_SERVER: Mutex<Option<TestServer>> = Mutex::const_new(None);

pub struct TestServer {
    pub port: u16,
    handle: tokio::task::JoinHandle<()>,
}

impl TestServer {
    pub async fn start(config: Config) -> Self {
        let port = config.sites[0].port;
        let server = Server::new(config).await.unwrap();
        
        let handle = tokio::spawn(async move {
            server.run().await.unwrap();
        });
        
        // Wait for server to start
        tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
        
        TestServer { port, handle }
    }
    
    pub fn url(&self) -> String {
        format!("http://127.0.0.1:{}", self.port)
    }
}

impl Drop for TestServer {
    fn drop(&mut self) {
        self.handle.abort();
    }
}

// Global test server for shared tests
pub async fn get_test_server() -> &'static TestServer {
    let mut server = TEST_SERVER.lock().await;
    if server.is_none() {
        *server = Some(TestServer::start(test_config()).await);
    }
    server.as_ref().unwrap()
}
}

Test File Management

#![allow(unused)]
fn main() {
use tempfile::{TempDir, NamedTempFile};

pub struct TestStaticFiles {
    pub temp_dir: TempDir,
    pub index_file: PathBuf,
    pub css_file: PathBuf,
    pub js_file: PathBuf,
}

impl TestStaticFiles {
    pub async fn new() -> Self {
        let temp_dir = TempDir::new().unwrap();
        
        let index_file = temp_dir.path().join("index.html");
        tokio::fs::write(&index_file, "<h1>Test Index</h1>").await.unwrap();
        
        let css_file = temp_dir.path().join("style.css");
        tokio::fs::write(&css_file, "body { color: red; }").await.unwrap();
        
        let js_file = temp_dir.path().join("script.js");
        tokio::fs::write(&js_file, "console.log('test');").await.unwrap();
        
        TestStaticFiles {
            temp_dir,
            index_file,
            css_file,
            js_file,
        }
    }
    
    pub fn path(&self) -> &Path {
        self.temp_dir.path()
    }
}
}

Continuous Integration Testing

GitHub Actions Test Workflow

# .github/workflows/test.yml
name: Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    name: Test Suite
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        rust: [stable, beta]
        
    steps:
    - uses: actions/checkout@v4
    
    - name: Install Rust
      uses: dtolnay/rust-toolchain@master
      with:
        toolchain: ${{ matrix.rust }}
        components: rustfmt, clippy
    
    - name: Cache dependencies
      uses: actions/cache@v3
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
          target
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
    
    - name: Check formatting
      run: cargo fmt --all -- --check
    
    - name: Run clippy
      run: cargo clippy --all-targets --all-features -- -D warnings
    
    - name: Run tests
      run: cargo test --all-features --verbose
    
    - name: Run integration tests
      run: cargo test --test integration --all-features
    
    - name: Run benchmarks (check only)
      run: cargo bench --no-run

Test Coverage

# Add to GitHub Actions
- name: Install coverage tools
  run: |
    cargo install cargo-tarpaulin

- name: Generate test coverage
  run: |
    cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Xml

- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v3
  with:
    file: ./cobertura.xml

Testing Best Practices

Test Organization

  • Group related tests in modules
  • Use descriptive test names
  • Follow AAA pattern (Arrange, Act, Assert)
  • Test both happy path and error cases
  • Use test fixtures for common setup

Test Data Management

  • Use temporary directories for file operations
  • Clean up resources in test teardown
  • Use factories for creating test objects
  • Avoid hardcoded values, use constants

Performance Testing

  • Run performance tests in isolated environment
  • Use consistent hardware for benchmarks
  • Monitor for performance regressions
  • Set reasonable performance thresholds

Security Testing

  • Test all input validation
  • Check authentication and authorization
  • Verify secure defaults
  • Test rate limiting and DOS protection

Test Maintenance

  • Keep tests up to date with code changes
  • Remove or update obsolete tests
  • Refactor duplicated test code
  • Document complex test scenarios

Next Steps

Troubleshooting

This guide helps you diagnose and resolve common issues with BWS installation, configuration, and operation.

Common Issues

Installation Problems

Rust Installation Issues

Problem: cargo command not found

cargo: command not found

Solution:

# Install Rust via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

# Or add to shell profile
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Problem: Outdated Rust version

error: package `bws v0.1.5` cannot be built because it requires rustc 1.89 or newer

Solution:

# Update Rust toolchain
rustup update stable
rustc --version  # Should show 1.89 or newer

Compilation Errors

Problem: Missing system dependencies

error: failed to run custom build command for `openssl-sys v0.9.xx`

Solution:

# Ubuntu/Debian
sudo apt update
sudo apt install -y pkg-config libssl-dev build-essential

# CentOS/RHEL/Fedora
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y pkg-config openssl-devel

# macOS
brew install openssl pkg-config

Problem: Linker errors

error: linking with `cc` failed: exit status: 1
note: /usr/bin/ld: cannot find -lssl

Solution:

# Ubuntu/Debian
sudo apt install -y libssl-dev

# Set environment variables if needed
export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig"
export OPENSSL_DIR="/usr"

Configuration Issues

Invalid TOML Syntax

Problem: Configuration file parsing errors

Error: failed to parse config file
Caused by: invalid TOML syntax at line 5, column 10

Solution:

# Check TOML syntax
# Common issues:
# 1. Missing quotes around strings
name = example     # Wrong
name = "example"   # Correct

# 2. Incorrect array syntax
ports = [8080 8081]     # Wrong (missing comma)
ports = [8080, 8081]    # Correct

# 3. Misplaced sections
[[sites]]
name = "test"
[daemon]           # Wrong (should be before [[sites]])

# Validate TOML syntax online: https://www.toml-lint.com/

Missing Required Fields

Problem: Configuration validation errors

Error: missing required field `port` for site 'example'

Solution:

# Ensure all required fields are present
[[sites]]
name = "example"      # Required
hostname = "localhost" # Required
port = 8080           # Required
static_dir = "static" # Required

Port Binding Issues

Problem: Address already in use

Error: failed to bind to 127.0.0.1:8080
Caused by: Address already in use (os error 98)

Solution:

# Check what's using the port
lsof -i :8080
netstat -tulpn | grep :8080

# Kill the process using the port
kill -9 $(lsof -ti:8080)

# Or use a different port
[[sites]]
name = "example"
hostname = "localhost"
port = 8081  # Use different port
static_dir = "static"

File Permission Issues

Problem: Cannot access static files

Error: Permission denied (os error 13)

Solution:

# Check file permissions
ls -la static/

# Fix permissions
chmod 755 static/
chmod 644 static/*
find static/ -type d -exec chmod 755 {} \;
find static/ -type f -exec chmod 644 {} \;

# Check directory ownership
sudo chown -R $USER:$USER static/

Runtime Issues

BWS Won't Start

Problem: Server fails to start silently

# No output, process exits immediately

Solution:

# Enable debug logging
RUST_LOG=debug ./target/release/bws --config config.toml

# Check configuration
./target/release/bws --config config.toml --validate

# Check system resources
df -h        # Disk space
free -h      # Memory
ulimit -n    # File descriptors

High Memory Usage

Problem: BWS consuming excessive memory

# Process using > 1GB RAM for simple static serving

Solution:

# Monitor memory usage
ps aux | grep bws
top -p $(pgrep bws)

# Check for memory leaks
valgrind --tool=memcheck --leak-check=full ./target/release/bws

# Optimize configuration
[performance]
max_connections = 1000      # Reduce if too high
worker_threads = 4          # Match CPU cores
connection_pool_size = 100  # Reduce pool size

[caching]
max_memory = "100MB"        # Limit cache size

High CPU Usage

Problem: BWS using 100% CPU

# CPU usage constantly high even with low traffic

Solution:

# Profile CPU usage
perf record -g ./target/release/bws --config config.toml
perf report

# Check configuration
[performance]
worker_threads = 4  # Don't exceed CPU cores
keep_alive_timeout = 30  # Reduce timeout

# Monitor system load
htop
iostat 1

Connection Issues

Problem: Cannot connect to BWS

curl: (7) Failed to connect to localhost port 8080: Connection refused

Solution:

# Check if BWS is running
ps aux | grep bws
systemctl status bws  # If using systemd

# Check port binding
netstat -tulpn | grep :8080
ss -tulpn | grep :8080

# Check firewall
sudo ufw status
sudo iptables -L

# Test locally first
curl -v http://127.0.0.1:8080/

Hot Reload Issues

Hot Reload Not Working

Problem: Configuration changes not applied after SIGHUP

kill -HUP $(pgrep -f "bws.*master")
# No new worker spawned, configuration not updated

Solution:

# Check if master process exists
pgrep -f "bws.*master"

# Verify BWS is running in master-worker mode
ps aux | grep bws | grep -v grep

# Check logs for errors
tail -f /var/log/bws/bws.log | grep -E "(reload|error|worker|master)"

# Validate configuration before reload
bws --config-check /etc/bws/config.toml

# If master process not found, restart BWS
systemctl restart bws

Configuration Validation Failed

Problem: Invalid configuration preventing hot reload

tail -f /var/log/bws/bws.log
# ERROR: Configuration validation failed: ...

Solution:

# Test configuration syntax
bws --config-check /etc/bws/config.toml

# Common validation issues:
# - Invalid port numbers
# - Missing static directories
# - Invalid SSL certificate paths
# - Malformed TOML syntax

# Fix configuration and retry
vim /etc/bws/config.toml
bws --config-check /etc/bws/config.toml
systemctl reload bws

Worker Process Issues

Problem: New worker fails to start during reload

# Master spawns worker but worker exits immediately

Solution:

# Check worker process logs
journalctl -u bws -f | grep worker

# Common causes:
# 1. Port already in use by another process
netstat -tulpn | grep :8080

# 2. Permission issues
# Check file permissions for static directories
ls -la /var/www/html/

# 3. SSL certificate issues
# Verify certificate files exist and are readable
ls -la /etc/ssl/certs/example.com.crt
ls -la /etc/ssl/private/example.com.key

# 4. Resource limits
ulimit -n  # Check file descriptor limit

Master Process Not Responding

Problem: Master process exists but doesn't respond to signals

pgrep -f "bws.*master"  # Shows PID
kill -HUP <PID>         # No response

Solution:

# Check if process is stuck
ps aux | grep bws
top -p $(pgrep -f "bws.*master")

# Check system resources
free -h
df -h

# Force restart if unresponsive
systemctl stop bws
sleep 5
systemctl start bws

# Check process tree after restart
pstree -p $(pgrep -f "bws.*master")

Configuration Not Updating

Problem: Hot reload succeeds but changes not visible

# No errors in logs, new worker spawned, but config unchanged

Solution:

# Verify configuration file is correct
cat /etc/bws/config.toml

# Check if BWS is reading the right config file
ps aux | grep bws | grep -o -- '--config [^ ]*'

# Test specific changes
curl -I http://localhost:8080/ | grep "X-Custom-Header"

# Check if browser is caching
curl -H "Cache-Control: no-cache" http://localhost:8080/

# Verify worker process PID changed
# Before reload: note worker PID
ps aux | grep bws
# After reload: verify PID is different

Performance Issues

Slow Response Times

Problem: High latency for static file serving

# Response times > 1 second for small files

Solution:

# Check disk I/O
iostat -x 1
iotop

# Optimize storage
# Use SSD for static files
# Enable file system caching
mount -o remount,noatime /path/to/static

# Tune BWS configuration
[performance]
read_buffer_size = "64KB"   # Increase buffer size
write_buffer_size = "64KB"
worker_threads = 8          # Increase workers

[caching]
enabled = true
max_memory = "1GB"          # Enable caching

Low Throughput

Problem: Cannot handle expected load

# Failing under moderate load (< 1000 req/s)

Solution:

# Increase system limits
# /etc/security/limits.conf
bws soft nofile 65536
bws hard nofile 65536

# /etc/sysctl.conf
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# Tune BWS
[performance]
max_connections = 10000
worker_threads = 16         # 2x CPU cores
keep_alive_timeout = 60

# Use load balancer for scaling

Network Issues

Timeout Errors

Problem: Requests timing out

curl: (28) Operation timed out after 30000 milliseconds

Solution:

# Check network connectivity
ping hostname
traceroute hostname

# Increase timeouts
[performance]
request_timeout = 60      # Increase from 30
response_timeout = 60
keep_alive_timeout = 120

# Check for network congestion
iftop
nethogs

DNS Resolution Issues

Problem: Cannot resolve hostname

curl: (6) Could not resolve host: example.com

Solution:

# Test DNS resolution
nslookup example.com
dig example.com

# Check /etc/hosts
grep example.com /etc/hosts

# Use IP address instead
[[sites]]
name = "example"
hostname = "192.168.1.100"  # Use IP instead of hostname
port = 8080
static_dir = "static"

SSL/TLS Issues

Certificate Problems

Problem: SSL certificate errors

Error: SSL certificate verification failed

Solution:

# Check certificate validity
openssl x509 -in cert.pem -text -noout
openssl verify -CAfile ca.pem cert.pem

# Check certificate permissions
ls -la /etc/ssl/certs/
chmod 644 /etc/ssl/certs/cert.pem
chmod 600 /etc/ssl/private/key.pem

# Verify certificate chain
openssl s_client -connect example.com:443 -showcerts

TLS Handshake Failures

Problem: TLS handshake errors

Error: TLS handshake failed

Solution:

# Check TLS configuration
[sites.ssl]
enabled = true
protocols = ["TLSv1.2", "TLSv1.3"]  # Ensure modern protocols
cert_file = "/path/to/cert.pem"
key_file = "/path/to/key.pem"

# Test TLS connection
openssl s_client -connect localhost:8443 -tls1_2

Docker Issues

Container Won't Start

Problem: Docker container exits immediately

docker run bws:latest
# Container exits with code 1

Solution:

# Check container logs
docker logs container_id

# Run interactively for debugging
docker run -it --entrypoint /bin/bash bws:latest

# Check file permissions in container
docker run --rm bws:latest ls -la /app/

# Mount configuration correctly
docker run -v $(pwd)/config.toml:/app/config.toml bws:latest

Volume Mount Issues

Problem: Cannot access mounted files

Error: No such file or directory: /app/static/index.html

Solution:

# Check volume mount syntax
docker run -v $(pwd)/static:/app/static bws:latest

# Verify host path exists
ls -la $(pwd)/static/

# Check file permissions
chmod -R 644 static/
chmod 755 static/

# Use absolute paths
docker run -v /full/path/to/static:/app/static bws:latest

Diagnostic Commands

System Information

#!/bin/bash
# diagnostic.sh - System diagnostic script

echo "=== BWS Diagnostic Information ==="
echo "Date: $(date)"
echo "Host: $(hostname)"
echo

echo "=== System Information ==="
uname -a
cat /etc/os-release 2>/dev/null || cat /etc/redhat-release 2>/dev/null
echo

echo "=== Resource Usage ==="
echo "CPU cores: $(nproc)"
echo "Memory:"
free -h
echo "Disk:"
df -h
echo "Load average:"
uptime
echo

echo "=== Network Configuration ==="
ip addr show
echo
netstat -tulpn | grep -E ":(8080|8081|8082|8083)"
echo

echo "=== BWS Process Information ==="
if pgrep -f bws > /dev/null; then
    echo "BWS is running:"
    ps aux | grep -v grep | grep bws
    echo
    echo "Open files:"
    lsof -p $(pgrep bws) | head -20
else
    echo "BWS is not running"
fi
echo

echo "=== Configuration Files ==="
find . -name "*.toml" -exec echo "File: {}" \; -exec head -20 {} \; -exec echo \;

echo "=== Log Files ==="
find /var/log -name "*bws*" 2>/dev/null | while read log; do
    echo "=== $log ==="
    tail -20 "$log" 2>/dev/null
    echo
done

echo "=== Recent System Logs ==="
journalctl -u bws --no-pager -n 20 2>/dev/null || echo "No systemd logs found"

Network Diagnostics

#!/bin/bash
# network-test.sh - Network connectivity test

HOST=${1:-localhost}
PORT=${2:-8080}

echo "Testing connectivity to $HOST:$PORT"

# Test basic connectivity
echo "=== Ping Test ==="
ping -c 3 $HOST

echo "=== Port Test ==="
nc -zv $HOST $PORT 2>&1

echo "=== HTTP Test ==="
curl -v http://$HOST:$PORT/ 2>&1 | head -20

echo "=== DNS Resolution ==="
nslookup $HOST

echo "=== Route Trace ==="
traceroute $HOST 2>/dev/null | head -10

Performance Monitoring

#!/bin/bash
# monitor.sh - Real-time BWS monitoring

BWS_PID=$(pgrep bws)

if [ -z "$BWS_PID" ]; then
    echo "BWS process not found"
    exit 1
fi

echo "Monitoring BWS process $BWS_PID"
echo "Press Ctrl+C to stop"

while true; do
    clear
    echo "=== BWS Monitoring - $(date) ==="
    echo
    
    # Process information
    echo "=== Process Information ==="
    ps -o pid,ppid,cmd,%mem,%cpu,time -p $BWS_PID
    echo
    
    # Memory usage
    echo "=== Memory Usage ==="
    cat /proc/$BWS_PID/status | grep -E "VmSize|VmRSS|VmData|VmStk"
    echo
    
    # Network connections
    echo "=== Network Connections ==="
    ss -tuln | grep ":8080\|:8081\|:8082\|:8083" | wc -l | xargs echo "Active connections:"
    echo
    
    # File descriptors
    echo "=== File Descriptors ==="
    ls /proc/$BWS_PID/fd/ | wc -l | xargs echo "Open file descriptors:"
    echo
    
    # System load
    echo "=== System Load ==="
    uptime
    
    sleep 5
done

Log Analysis

Error Log Patterns

# Common error patterns to look for

# Configuration errors
grep -i "config\|parse\|invalid" /var/log/bws/bws.log

# Network errors
grep -i "bind\|connection\|timeout" /var/log/bws/bws.log

# File system errors
grep -i "permission\|not found\|access" /var/log/bws/bws.log

# Performance issues
grep -i "slow\|timeout\|overload" /var/log/bws/bws.log

# Security issues
grep -i "attack\|malicious\|blocked" /var/log/bws/bws.log

Log Analysis Script

#!/bin/bash
# analyze-logs.sh - BWS log analysis

LOG_FILE=${1:-/var/log/bws/bws.log}

if [ ! -f "$LOG_FILE" ]; then
    echo "Log file not found: $LOG_FILE"
    exit 1
fi

echo "Analyzing BWS logs: $LOG_FILE"
echo "Log file size: $(du -h $LOG_FILE | cut -f1)"
echo "Total lines: $(wc -l < $LOG_FILE)"
echo

echo "=== Error Summary ==="
grep -i error "$LOG_FILE" | wc -l | xargs echo "Total errors:"
grep -i warn "$LOG_FILE" | wc -l | xargs echo "Total warnings:"
echo

echo "=== Recent Errors ==="
grep -i error "$LOG_FILE" | tail -10
echo

echo "=== Top Error Messages ==="
grep -i error "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo

echo "=== Request Statistics ==="
if grep -q "request" "$LOG_FILE"; then
    grep "request" "$LOG_FILE" | wc -l | xargs echo "Total requests:"
    
    # Status code distribution
    echo "Status codes:"
    grep -o '"status":[0-9]*' "$LOG_FILE" | cut -d: -f2 | sort | uniq -c | sort -rn
fi

Recovery Procedures

Service Recovery

#!/bin/bash
# recover-bws.sh - BWS service recovery

echo "Starting BWS recovery procedure..."

# Stop any running instances
echo "Stopping BWS..."
systemctl stop bws 2>/dev/null || pkill -f bws

# Clean up PID files
rm -f /var/run/bws.pid

# Check configuration
echo "Validating configuration..."
if ! bws --config /etc/bws/config.toml --validate; then
    echo "Configuration invalid, please fix and retry"
    exit 1
fi

# Check file permissions
echo "Checking file permissions..."
if [ ! -r /etc/bws/config.toml ]; then
    echo "Configuration file not readable"
    chmod 644 /etc/bws/config.toml
fi

# Check static directory
STATIC_DIR=$(grep static_dir /etc/bws/config.toml | head -1 | cut -d'"' -f2)
if [ ! -d "$STATIC_DIR" ]; then
    echo "Creating static directory: $STATIC_DIR"
    mkdir -p "$STATIC_DIR"
    echo "<h1>BWS Recovery Page</h1>" > "$STATIC_DIR/index.html"
fi

# Start service
echo "Starting BWS..."
if systemctl start bws; then
    echo "BWS started successfully"
    systemctl status bws
else
    echo "Failed to start BWS, check logs:"
    journalctl -u bws --no-pager -n 20
fi

Database Recovery (if applicable)

#!/bin/bash
# recover-database.sh - Database recovery for BWS

echo "Database recovery procedure..."

# Check database connection
if ! pg_isready -h localhost -p 5432; then
    echo "Database not accessible"
    exit 1
fi

# Backup current database
pg_dump bws > /backup/bws-recovery-$(date +%Y%m%d).sql

# Run database maintenance
psql bws -c "VACUUM ANALYZE;"
psql bws -c "REINDEX DATABASE bws;"

echo "Database recovery completed"

Prevention Strategies

Monitoring Setup

# Set up basic monitoring
crontab -e

# Add monitoring jobs
*/5 * * * * /usr/local/bin/bws-health-check || /usr/bin/logger "BWS health check failed"
0 */6 * * * /usr/local/bin/cleanup-logs
0 2 * * * /usr/local/bin/backup-bws-config

Automated Backups

#!/bin/bash
# backup-bws.sh - Automated BWS backup

BACKUP_DIR="/backup/bws"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

# Backup configuration
tar -czf "$BACKUP_DIR/config-$DATE.tar.gz" /etc/bws/

# Backup static files
tar -czf "$BACKUP_DIR/static-$DATE.tar.gz" /var/www/

# Backup logs
tar -czf "$BACKUP_DIR/logs-$DATE.tar.gz" /var/log/bws/

# Clean old backups (keep 30 days)
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete

echo "Backup completed: $BACKUP_DIR"

Health Monitoring

#!/bin/bash
# health-monitor.sh - Continuous health monitoring

ALERT_EMAIL="admin@example.com"
ALERT_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"

check_health() {
    if ! curl -f -s http://localhost:8080/health > /dev/null; then
        return 1
    fi
    return 0
}

send_alert() {
    local message="$1"
    
    # Email alert
    echo "$message" | mail -s "BWS Alert" "$ALERT_EMAIL"
    
    # Slack alert
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"$message\"}" \
        "$ALERT_WEBHOOK"
}

# Main monitoring loop
while true; do
    if ! check_health; then
        send_alert "BWS health check failed at $(date)"
        
        # Attempt recovery
        systemctl restart bws
        sleep 30
        
        if check_health; then
            send_alert "BWS recovered successfully at $(date)"
        else
            send_alert "BWS recovery failed - manual intervention required"
        fi
    fi
    
    sleep 60
done

Getting Help

Information to Collect

When reporting issues, include:

  1. System Information

    • Operating system and version
    • Rust version (rustc --version)
    • BWS version (bws --version)
  2. Configuration

    • Configuration file content (sanitized)
    • Command line arguments used
  3. Error Information

    • Complete error messages
    • Stack traces if available
    • Log file contents
  4. 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

  1. Search existing issues
  2. Try the latest version
  3. Check documentation
  4. Provide minimal reproduction case
  5. Include diagnostic information

Next Steps

After resolving issues:

  1. Update your monitoring setup
  2. Review Performance Tuning
  3. Check Security Best Practices
  4. Consider High Availability Setup

Frequently Asked Questions (FAQ)

General Questions

What is BWS?

BWS (Basic Web Server) is a high-performance, multi-site web server built with Rust and powered by Cloudflare's Pingora framework. It's designed for serving static files and provides enterprise-grade features including multi-site hosting, SSL/TLS support, and comprehensive monitoring.

Why use BWS over other web servers?

Performance: Built on Pingora, BWS offers exceptional performance and low latency.

Safety: Written in Rust, BWS provides memory safety and prevents common security vulnerabilities.

Simplicity: Easy configuration through TOML files and straightforward deployment.

Multi-tenancy: Native support for hosting multiple sites from a single instance.

Modern Features: Built-in support for WASM, comprehensive MIME types, and modern web standards.

Can I quickly serve a directory without configuration?

Yes! BWS supports instant directory serving. Simply run:

# Serve current directory
bws .

# Serve a specific directory
bws /path/to/your/files

# On Windows
bws C:\MyWebsite

This bypasses the need for configuration files and instantly starts serving static files on http://localhost:8080.

What are the system requirements?

Minimum Requirements:

  • CPU: 1 core (x86_64 or ARM64)
  • RAM: 256MB
  • Storage: 100MB for binary + static content
  • OS: Linux, macOS, or Windows

Recommended for Production:

  • CPU: 4+ cores
  • RAM: 2GB+
  • Storage: SSD with sufficient space for content
  • OS: Linux (Ubuntu 20.04+, CentOS 8+, RHEL 8+)

Is BWS production-ready?

Yes, BWS is production-ready. It's built on Cloudflare's battle-tested Pingora framework and includes:

  • Comprehensive error handling
  • Security hardening
  • Performance optimization
  • Monitoring and logging
  • Automated testing
  • Documentation and support

Installation & Setup

How do I install BWS?

You have several options:

From pre-built binaries:

# Download from GitHub releases
wget https://github.com/yourusername/bws/releases/latest/download/bws-linux-x86_64.tar.gz
tar -xzf bws-linux-x86_64.tar.gz

From source:

git clone https://github.com/yourusername/bws.git
cd bws
cargo build --release

Using Docker:

docker pull ghcr.io/yourusername/bws:latest

See the Installation Guide for detailed instructions.

Do I need root privileges to run BWS?

No, BWS can run as a non-root user. However:

  • Ports < 1024: Require root privileges or CAP_NET_BIND_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:

  1. Generate configuration programmatically:
envsubst < config.template.toml > config.toml
  1. Use multiple config files:
bws --config config-${ENVIRONMENT}.toml
  1. Override specific settings via command line (if supported in your version)

How do I enable SSL/TLS?

Add SSL configuration to your site:

[[sites]]
name = "secure"
hostname = "example.com"
port = 8443
static_dir = "/var/www/secure"

[sites.ssl]
enabled = true
cert_file = "/etc/ssl/certs/example.com.pem"
key_file = "/etc/ssl/private/example.com.key"
protocols = ["TLSv1.2", "TLSv1.3"]

See SSL/TLS Configuration for details.

How does hot reload work?

BWS uses a master-worker architecture for true hot reload:

  1. Master Process: Monitors configuration and manages workers
  2. Worker Processes: Handle HTTP requests and connections
  3. Hot Reload: Master spawns new workers with updated config
  4. Zero Downtime: Old workers finish existing requests gracefully

Trigger hot reload with:

# Send SIGHUP to master process
kill -HUP $(pgrep -f "bws.*master")

# Or using systemctl
systemctl reload bws

What can be hot reloaded?

โœ… Supported:

  • Site configurations and hostnames
  • SSL certificate paths
  • Proxy upstream configurations
  • Static file directories
  • Security headers
  • Load balancing settings

โŒ Requires restart:

  • Server listen ports
  • Core server settings

How do I check if hot reload succeeded?

Monitor the process:

# Check process tree
pstree -p $(pgrep -f "bws.*master")

# Watch logs
tail -f /var/log/bws/bws.log | grep -E "(reload|worker|master)"

# Test configuration
curl http://localhost:8080/health

What's the difference between hostname and domain?

  • hostname: The network interface and host header to match
  • domain: An alias for hostname (legacy compatibility)

Both fields serve the same purpose. Use hostname for new configurations.

How do I serve files from subdirectories?

BWS automatically serves files from subdirectories. For example, with:

static_dir = "/var/www/html"

These URLs work automatically:

  • http://example.com/ โ†’ /var/www/html/index.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 with application/wasm
  • Supports WebAssembly streaming compilation
  • Enables modern web applications

No additional configuration required - WASM support is built-in.

Security

Is BWS secure?

BWS implements multiple security layers:

Language Safety: Written in Rust, preventing memory safety vulnerabilities.

Input Validation: Strict validation of requests and configuration.

Path Traversal Protection: Prevents access to files outside static directories.

Security Headers: Configurable security headers.

Regular Updates: Dependencies are regularly updated for security fixes.

How do I secure BWS in production?

Basic security:

[security]
hide_server_header = true    # Don't reveal server information
max_request_size = "10MB"    # Prevent large request attacks

[headers]
"X-Frame-Options" = "DENY"
"X-Content-Type-Options" = "nosniff"
"X-XSS-Protection" = "1; mode=block"
"Strict-Transport-Security" = "max-age=31536000; includeSubDomains"

Additional measures:

  • Run as non-root user
  • Use firewall to restrict access
  • Enable SSL/TLS with strong ciphers
  • Regular security updates
  • Monitor logs for suspicious activity

See Security Best Practices for comprehensive guidance.

Does BWS log sensitive information?

BWS is designed to avoid logging sensitive information:

  • Passwords and API keys are not logged
  • Request bodies are not logged by default
  • IP addresses are logged for legitimate monitoring

Log configuration:

[logging]
level = "info"               # Avoid "debug" in production
access_log = "/var/log/bws/access.log"
error_log = "/var/log/bws/error.log"

Always review logs before sharing for troubleshooting.

Can I restrict access to certain files?

Currently, BWS serves all files from the static directory. For access control:

File system permissions:

# Remove read permissions for sensitive files
chmod 600 sensitive-file.txt

Reverse proxy approach: Use nginx or Apache as a reverse proxy for advanced access control:

location /admin/ {
    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://localhost:8080;
}

Separate directories: Use different BWS sites for different access levels.

Deployment

How do I deploy BWS in production?

Systemd service (recommended for Linux):

# Create service file
sudo tee /etc/systemd/system/bws.service > /dev/null <<EOF
[Unit]
Description=BWS Web Server
After=network.target

[Service]
Type=simple
User=bws
ExecStart=/usr/local/bin/bws --config /etc/bws/config.toml
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# Enable and start service
sudo systemctl enable bws
sudo systemctl start bws

Docker deployment:

docker run -d 
  --name bws 
  -p 8080:8080 
  -v /path/to/config.toml:/app/config.toml:ro 
  -v /path/to/static:/app/static:ro 
  ghcr.io/yourusername/bws:latest

See Production Deployment for complete instructions.

Can I use BWS with a reverse proxy?

Yes, BWS works well behind reverse proxies:

Nginx example:

upstream bws {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://bws;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Benefits:

  • SSL termination at proxy
  • Load balancing across multiple BWS instances
  • Advanced routing and caching
  • Security filtering

How do I update BWS?

Binary updates:

# Stop service
sudo systemctl stop bws

# Backup current binary
cp /usr/local/bin/bws /usr/local/bin/bws.backup

# Replace binary
wget https://github.com/yourusername/bws/releases/latest/download/bws-linux-x86_64.tar.gz
tar -xzf bws-linux-x86_64.tar.gz
sudo cp bws /usr/local/bin/

# Restart service
sudo systemctl start bws

Docker updates:

# Pull new image
docker pull ghcr.io/yourusername/bws:latest

# Restart with new image
docker-compose down
docker-compose up -d

Source updates:

git pull origin main
cargo build --release
sudo cp target/release/bws /usr/local/bin/
sudo systemctl restart bws

Can I run multiple BWS instances?

Yes, you can run multiple BWS instances:

Same machine, different ports:

# Instance 1: config1.toml
[[sites]]
name = "site1"
hostname = "localhost"
port = 8080
static_dir = "/var/www/site1"

# Instance 2: config2.toml
[[sites]]
name = "site2"
hostname = "localhost"
port = 8081
static_dir = "/var/www/site2"

Load balancing: Use a load balancer to distribute traffic across instances.

Different machines: Deploy separate BWS instances on different servers for horizontal scaling.

Troubleshooting

BWS directory serving doesn't work - what to check?

When using bws [path] for instant directory serving:

  1. Directory doesn't exist:
# Verify the path exists
ls -la /path/to/directory  # Linux/macOS
dir C:\path\to\directory   # Windows
  1. Permission issues:
# Check read permissions
ls -la /path/to/directory
# Ensure BWS can read the directory contents
  1. Port already in use:
# Default port 8080 might be occupied
netstat -an | grep :8080     # Linux/macOS  
netstat -an | findstr :8080  # Windows
  1. Windows path issues:
  • Use forward slashes: bws C:/MyWebsite
  • Or escape backslashes: bws "C:\\MyWebsite"
  • BWS automatically handles Windows extended paths

BWS won't start - what should I check?

Common issues:

  1. Configuration errors:
bws --config config.toml --validate
  1. Port already in use:
lsof -i :8080
  1. File permissions:
ls -la config.toml
ls -la /path/to/static/
  1. 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:

  1. Fork the repository on GitHub
  2. Clone your fork: git clone https://github.com/yourusername/bws.git
  3. Create a feature branch: git checkout -b feature/amazing-feature
  4. Make your changes and add tests
  5. Run tests: cargo test
  6. 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:

  1. Check existing issues on GitHub
  2. Try the latest version
  3. Read the documentation
  4. Gather diagnostic information

Bug report should include:

  • BWS version (bws --version)
  • Operating system and version
  • Configuration file (sanitized)
  • Complete error messages
  • Steps to reproduce
  • Expected vs. actual behavior

Submit issues at: https://github.com/yourusername/bws/issues

License & Support

What license is BWS under?

BWS is released under the MIT License, which allows:

  • Commercial use
  • Modification
  • Distribution
  • Private use

With requirements for:

  • Including the license notice
  • Including the copyright notice

Where can I get support?

Free support:

  • GitHub Issues (bug reports)
  • GitHub Discussions (questions)
  • Documentation (comprehensive guides)
  • Community forums

Commercial support: Contact us for:

  • Priority support
  • Custom development
  • Training and consulting
  • Enterprise licensing

How often is BWS updated?

Regular releases:

  • Patch releases: Monthly (bug fixes)
  • Minor releases: Quarterly (new features)
  • Major releases: Yearly (breaking changes)

Security updates:

  • Critical security fixes: Within 24-48 hours
  • Regular security updates: Weekly

Dependencies:

  • Rust toolchain: Follow Rust stable releases
  • Pingora framework: Updated with upstream releases

Stay updated by:

  • Watching the GitHub repository
  • Following release notes
  • Subscribing to security advisories

Migration

Migrating from Apache/Nginx?

Configuration mapping:

Apache .htaccess โ†’ BWS configuration:

# Apache
DocumentRoot /var/www/html
Listen 80

# BWS
[[sites]]
name = "main"
hostname = "localhost"
port = 8080
static_dir = "/var/www/html"

Common features:

  • Virtual hosts โ†’ Multiple sites
  • SSL configuration โ†’ SSL section
  • Custom headers โ†’ Headers section
  • Access logs โ†’ Logging configuration

Migrating from other Rust web servers?

From Actix-web:

  • Replace route handlers with static file serving
  • Migrate middleware to configuration
  • Update deployment scripts

From Warp:

  • Convert filters to BWS configuration
  • Replace custom handlers with static serving
  • Update error handling

From Rocket:

  • Replace routes with static file configuration
  • Migrate state management to external systems
  • Update launch configuration

Migrating configuration formats?

From JSON:

# Convert JSON to TOML
pip install json2toml
json2toml config.json config.toml

From YAML:

# Convert YAML to TOML
pip install yq
yq -t config.yaml > config.toml

Manual migration: Review and adapt configuration according to BWS schema.

Future Roadmap

What's planned for future versions?

Short-term (next 3 months):

  • Enhanced monitoring and metrics
  • Additional security features
  • Performance optimizations
  • Configuration validation improvements

Medium-term (next 6 months):

  • Plugin system
  • Advanced caching strategies
  • HTTP/3 support
  • Enhanced SSL/TLS configuration

Long-term (next year):

  • GUI configuration interface
  • Distributed deployment support
  • Advanced load balancing
  • Enterprise features

How can I influence the roadmap?

  • Submit feature requests on GitHub
  • Participate in discussions
  • Contribute code
  • Sponsor development
  • Provide feedback and use cases

Don't see your question? Ask on GitHub Discussions or check the documentation.

Changelog

All notable changes to BWS will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.3.4] - 2025-08-26

Added

  • ๐Ÿ” Configuration Validation: Comprehensive --dry-run feature for validating configurations without starting server
  • ๐Ÿ“Š Enhanced API Endpoints: Added /api/health/detailed and /api/sites endpoints for better monitoring
  • ๐Ÿ”„ Hot Reload API: Added /api/reload endpoint for real-time configuration reloading
  • ๐Ÿ”’ Secure Management API: Localhost-only Management API on port 7654 for administrative operations
    • Restricted to 127.0.0.1 binding for security
    • Optional API key authentication
    • Comprehensive audit logging with client IP tracking
    • Configuration reload endpoint: POST /api/config/reload
  • ๐Ÿงช Professional Test Suite: Organized test scripts in tests/scripts/ directory with comprehensive coverage
  • ๐Ÿ“š Enhanced Documentation: Updated all documentation to reflect current codebase and new features
  • โš™๏ธ Simplified Architecture: Removed deprecated bws-ctl binary, focusing on single bws binary
  • ๐ŸŽฏ GitHub Actions Integration: Professional CI/CD workflows with comprehensive testing and validation

Changed

  • ๐Ÿ”ง Configuration Schema: Enhanced validation with detailed error reporting and warnings
  • ๐Ÿ“ Test Organization: Reorganized test infrastructure with proper script categorization
  • ๐Ÿ—๏ธ Build Process: Streamlined build process focusing on single binary output
  • ๐Ÿ“– Documentation Structure: Comprehensive documentation updates reflecting current features

Removed

  • โŒ bws-ctl Binary: Deprecated control binary, functionality integrated into main bws binary
  • ๐Ÿงน Legacy Code: Removed unused and deprecated code paths
  • ๐Ÿ“‚ Outdated Tests: Cleaned up redundant and non-functional test files

Fixed

  • โœ… Configuration Validation: Robust error handling and validation throughout configuration system
  • ๐Ÿ”ง Script Permissions: All test scripts now have proper executable permissions
  • ๐Ÿ“Š API Consistency: Standardized API response formats and error handling

[Unreleased]

Added

  • ๏ฟฝ True Hot Reload: Master-worker architecture for zero-downtime configuration updates
  • ๐Ÿ—๏ธ Master-Worker Process Model: Production-grade process management with worker lifecycle control
  • โšก Zero-Downtime Operations: Configuration and binary updates without dropping connections
  • ๐Ÿ”„ Graceful Worker Replacement: Spawn new workers while old workers finish existing requests
  • ๐Ÿ“Š Process Monitoring: Comprehensive process tree monitoring and management
  • ๏ฟฝ๐Ÿ›ก๏ธ Production-Grade Error Handling: Comprehensive error handling throughout codebase - no more .unwrap() calls
  • ๐Ÿ”„ Automatic SSL Certificate Monitoring: Background certificate renewal service with robust error handling
  • ๐Ÿงน Code Quality Improvements: Zero Clippy warnings achieved for maximum code quality
  • ๐Ÿ”ง Thread-Safe SSL Operations: Fixed critical concurrency issues in certificate management
  • ๐Ÿ“š Enhanced Documentation: Updated documentation reflecting improved robustness and reliability
  • ๏ฟฝ WebSocket Proxy Support: Full WebSocket proxying framework with automatic upgrade detection
  • โš–๏ธ WebSocket Load Balancing: All load balancing algorithms extended to WebSocket connections
  • ๐Ÿ”„ Protocol Transformation: Automatic HTTP to WebSocket URL conversion (httpโ†’ws, httpsโ†’wss)
  • ๐Ÿค Bidirectional Framework: Foundation for real-time message forwarding (streaming pending)
  • ๐Ÿงช WebSocket Testing: Comprehensive test suite and interactive test script
  • ๐Ÿ“– WebSocket Documentation: Complete documentation with examples and configuration guides
  • ๏ฟฝ๐Ÿ”„ Reverse Proxy Functionality: Complete Caddy-style reverse proxy implementation
  • โš–๏ธ Load Balancing: Three algorithms - round-robin, weighted, and least-connections
  • ๐Ÿ”— Connection Tracking: Real-time connection monitoring for least-connections algorithm
  • ๐Ÿท๏ธ Header Management: Advanced proxy header forwarding and customization
  • โฑ๏ธ Request Timeouts: Configurable read/write timeouts for proxy requests
  • ๐Ÿ›ฃ๏ธ Path Transformation: URL rewriting and prefix stripping capabilities
  • ๐Ÿ”ง Per-Site Proxy Config: Individual proxy configuration for each site
  • ๐Ÿงช Load Balancing Tests: Comprehensive test suite for all load balancing methods
  • ๐Ÿ“š Proxy Documentation: Detailed documentation for reverse proxy and load balancing
  • Comprehensive documentation with mdBook
  • Advanced troubleshooting guides
  • Performance monitoring scripts
  • Security hardening guidelines

Changed

  • ๐Ÿš€ Enhanced Reliability: Replaced all dangerous .unwrap() calls with proper error handling
  • ๐Ÿ”’ SSL Manager Improvements: Fixed critical concurrency issues (Future not Send) in certificate operations
  • โšก Async Function Optimization: Improved async/await patterns throughout codebase
  • ๐Ÿ“ Modern String Formatting: Updated 50+ format strings to use modern interpolation
  • ๐Ÿ›ก๏ธ Thread-Safe Operations: All operations use proper atomic operations for concurrency
  • Enhanced Service Architecture: Integrated proxy handler with main web service
  • Thread-Safe Operations: All load balancing uses atomic operations for concurrency
  • Configuration Schema: Extended TOML schema to support proxy configurations
  • Improved error messages and diagnostics
  • Enhanced configuration validation
  • Better logging format and structure

Fixed

  • ๐Ÿšจ Critical SSL Concurrency Issues: Resolved "Future not Send" problems in SSL manager
  • ๐Ÿ›ก๏ธ Race Condition Elimination: Fixed race conditions in certificate validation and load balancing
  • ๐Ÿ’พ Resource Leak Prevention: Proper cleanup of certificate operations and connections
  • โšก Async Function Signatures: Fixed async/await patterns and removed unnecessary async functions
  • ๐Ÿ”ง Iterator and Formatting Issues: Resolved compilation errors from iterator usage
  • ๐Ÿ“ Error Documentation: Added comprehensive documentation for Result-returning functions
  • Proxy Error Handling: Graceful fallback with 502 Bad Gateway responses
  • Connection Cleanup: Proper connection tracking cleanup on request completion
  • Concurrent Safety: Race condition fixes in load balancing counters
  • Minor memory leaks in connection handling
  • Edge cases in path resolution
  • Configuration parsing edge cases

[0.1.5] - 2024-12-19

Added

  • WASM (WebAssembly) MIME type support (application/wasm)
  • Enhanced subdirectory file serving capabilities
  • Comprehensive error handling and logging
  • Security improvements and hardening
  • Performance optimizations
  • Docker containerization with multi-stage builds
  • GitHub Actions CI/CD pipeline with automated testing
  • Automated release workflow with GitHub Releases
  • Container registry publishing (GitHub Container Registry)
  • Supply chain security with attestations
  • Code coverage reporting and quality gates
  • Dependency vulnerability scanning

Changed

  • Improved MIME type detection and handling
  • Enhanced static file serving performance
  • Better error messages and user feedback
  • Upgraded to latest Pingora framework version
  • Optimized binary size and runtime performance

Removed

  • BREAKING: Removed /api/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:

  1. Read the changelog and release notes
  2. Check for breaking changes
  3. Backup configuration files
  4. 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