Quantum/CONTRIBUTING.md
RTSDA 85a4115a71 🚀 Initial release: Quantum Web Server v0.2.0
 Features:
• HTTP/1.1, HTTP/2, and HTTP/3 support with proper architecture
• Reverse proxy with advanced load balancing (round-robin, least-conn, etc.)
• Static file serving with content-type detection and security
• Revolutionary file sync system with WebSocket real-time updates
• Enterprise-grade health monitoring (active/passive checks)
• TLS/HTTPS with ACME/Let's Encrypt integration
• Dead simple JSON configuration + full Caddy v2 compatibility
• Comprehensive test suite (72 tests passing)

🏗️ Architecture:
• Rust-powered async performance with zero-cost abstractions
• HTTP/3 as first-class citizen with shared routing core
• Memory-safe design with input validation throughout
• Modular structure for easy extension and maintenance

📊 Status: 95% production-ready
🧪 Test Coverage: 72/72 tests passing (100% success rate)
🔒 Security: Memory safety + input validation + secure defaults

Built with ❤️ in Rust - Start simple, scale to enterprise!
2025-08-17 17:08:49 -04:00

12 KiB

Contributing to Caddy-RS

Thank you for your interest in contributing to Caddy-RS! This document provides guidelines and information for contributors.

Table of Contents

Code of Conduct

This project adheres to a code of conduct adapted from the Contributor Covenant. By participating, you are expected to uphold this code.

Our Pledge

  • Be welcoming and inclusive to all contributors regardless of experience level
  • Be respectful in all communications and code reviews
  • Be constructive when providing feedback
  • Focus on what is best for the community and the project

How to Contribute

There are many ways to contribute to Caddy-RS:

Types of Contributions

  1. Bug Reports: Help identify and fix issues
  2. Feature Requests: Suggest new functionality
  3. Code Contributions: Implement features, fix bugs, improve performance
  4. Documentation: Improve or add documentation
  5. Testing: Write tests, perform manual testing
  6. Performance: Optimize code, identify bottlenecks
  7. Security: Identify and fix security issues

Getting Started

  1. Look for good first issues: Check issues labeled good-first-issue or help-wanted
  2. Check existing issues: Avoid duplicate work by checking existing issues and PRs
  3. Join discussions: Participate in issue discussions to understand requirements
  4. Start small: Begin with small contributions to understand the codebase

Development Setup

Prerequisites

  • Rust 1.75+ with 2024 edition support
  • Git
  • A code editor (VS Code with rust-analyzer recommended)

Setup Steps

  1. Fork the repository on GitHub
  2. Clone your fork:
    git clone https://github.com/your-username/caddy-rs.git
    cd caddy-rs
    
  3. Add upstream remote:
    git remote add upstream https://github.com/original-owner/caddy-rs.git
    
  4. Install dependencies and build:
    cargo build
    cargo test
    

Development Tools

Install these tools for better development experience:

cargo install cargo-watch    # Auto-reload during development
cargo install cargo-edit     # Easy dependency management
cargo install cargo-audit    # Security vulnerability scanning
cargo install cargo-tarpaulin # Code coverage

Making Changes

Before You Start

  1. Create an issue if one doesn't exist for your change
  2. Discuss the approach in the issue before implementing
  3. Check for existing work to avoid duplication

Development Workflow

  1. Create a feature branch:

    git checkout -b feature/your-feature-name
    
  2. Make your changes:

    • Follow the coding standards
    • Write tests for new functionality
    • Update documentation as needed
  3. Test your changes:

    cargo test                    # Run all tests
    cargo clippy -- -D warnings  # Check for linting issues
    cargo fmt                     # Format code
    
  4. Commit your changes:

    git add .
    git commit -m "feat: add new feature description"
    

Commit Message Convention

We use conventional commits for clear, semantic commit messages:

<type>(<scope>): <description>

[optional body]

[optional footer]

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

Examples:

feat(proxy): add health check support
fix(config): handle missing configuration file gracefully
docs(api): update reverse proxy configuration examples
test(middleware): add unit tests for CORS middleware

Pull Request Process

Before Submitting

Ensure your PR meets these criteria:

  • Tests pass: cargo test
  • Code is formatted: cargo fmt
  • No linting warnings: cargo clippy -- -D warnings
  • Documentation updated: If you changed APIs or added features
  • Changelog updated: Add entry to CHANGELOG.md if needed
  • Performance impact considered: No significant performance regression

Submitting the PR

  1. Push to your fork:

    git push origin feature/your-feature-name
    
  2. Create pull request on GitHub with:

    • Clear title describing the change
    • Detailed description explaining what and why
    • Reference to related issues using #issue-number
    • Testing instructions for reviewers
    • Screenshots or examples if UI-related

PR Review Process

  1. Automated checks will run (tests, linting, etc.)
  2. Code review by maintainers and other contributors
  3. Address feedback by making additional commits
  4. Final approval and merge by maintainers

Review Guidelines for Contributors

When reviewing others' PRs:

  • Be kind and constructive in feedback
  • Focus on the code, not the person
  • Explain your suggestions with reasoning
  • Approve when ready or request changes with specific feedback
  • Test the changes if possible

Coding Standards

Rust Style Guidelines

Follow standard Rust conventions:

// Use snake_case for functions and variables
fn handle_request() -> Result<()> { }
let response_body = String::new();

// Use PascalCase for types and traits
struct ProxyService;
trait Middleware;
enum HandlerType;

// Use SCREAMING_SNAKE_CASE for constants
const MAX_RETRY_COUNT: u32 = 3;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);

Code Organization

// Order imports logically
use std::collections::HashMap;
use std::time::Duration;

use anyhow::Result;
use serde::{Deserialize, Serialize};
use tokio::net::TcpListener;

use crate::config::Config;
use crate::proxy::ProxyService;

// Group related functionality
impl ProxyService {
    // Public methods first
    pub async fn new(config: Config) -> Result<Self> { }
    pub async fn handle_request(&self) -> Result<()> { }
    
    // Private methods last
    async fn select_upstream(&self) -> Result<()> { }
}

Error Handling

Use Result types consistently:

use anyhow::{Context, Result};

// Good: Propagate errors with context
pub async fn load_config(path: &str) -> Result<Config> {
    let content = tokio::fs::read_to_string(path)
        .await
        .context("Failed to read configuration file")?;
    
    let config = serde_json::from_str(&content)
        .context("Failed to parse configuration")?;
    
    Ok(config)
}

// Avoid: Unwrapping or ignoring errors
let config = serde_json::from_str(&content).unwrap(); // Don't do this

Async Code

Follow async best practices:

// Good: Use async/await throughout
pub async fn proxy_request(&self, req: Request) -> Result<Response> {
    let upstream = self.select_upstream().await?;
    let response = self.client.request(upstream, req).await?;
    Ok(response)
}

// Avoid: Blocking calls in async context
std::thread::sleep(Duration::from_secs(1)); // Don't do this
tokio::time::sleep(Duration::from_secs(1)).await; // Do this instead

Documentation

Document public APIs:

/// Selects an upstream server using the configured load balancing algorithm.
/// 
/// This method applies the load balancing policy to choose from available
/// upstream servers. It considers server health and current load when making
/// the selection.
/// 
/// # Arguments
/// 
/// * `upstreams` - A slice of available upstream servers
/// * `policy` - The load balancing policy to apply
/// 
/// # Returns
/// 
/// Returns a reference to the selected upstream server, or an error if
/// no healthy upstreams are available.
/// 
/// # Examples
/// 
/// ```rust
/// let upstream = load_balancer.select_upstream(&upstreams, &policy)?;
/// println!("Selected: {}", upstream.dial);
/// ```
pub fn select_upstream<'a>(
    &self,
    upstreams: &'a [Upstream],
    policy: &LoadBalancingPolicy,
) -> Result<&'a Upstream> {
    // Implementation
}

Testing Guidelines

Test Categories

  1. Unit Tests: Test individual functions and modules
  2. Integration Tests: Test component interactions
  3. End-to-End Tests: Test complete workflows

Unit Test Examples

#[cfg(test)]
mod tests {
    use super::*;
    use tokio_test;
    
    #[tokio::test]
    async fn test_round_robin_selection() {
        let load_balancer = LoadBalancer::new();
        let upstreams = vec![
            Upstream { dial: "backend1:8080".to_string() },
            Upstream { dial: "backend2:8080".to_string() },
        ];
        
        let policy = LoadBalancingPolicy::RoundRobin;
        
        let first = load_balancer.select_upstream(&upstreams, &policy).unwrap();
        let second = load_balancer.select_upstream(&upstreams, &policy).unwrap();
        
        assert_ne!(first.dial, second.dial);
    }
    
    #[test]
    fn test_config_parsing() {
        let config_json = r#"
        {
            "listen": [":8080"],
            "routes": []
        }
        "#;
        
        let config: ServerConfig = serde_json::from_str(config_json).unwrap();
        assert_eq!(config.listen, vec![":8080"]);
    }
}

Testing Async Code

#[tokio::test]
async fn test_async_function() {
    let service = ProxyService::new(test_config()).await.unwrap();
    let request = test_request();
    
    let response = service.handle_request(request).await;
    
    assert!(response.is_ok());
}

Test Organization

  • Place unit tests in the same file using #[cfg(test)]
  • Place integration tests in separate files in tests/ directory
  • Use descriptive test names that explain what is being tested
  • Group related tests in modules

Documentation

Types of Documentation

  1. Code Documentation: Rustdoc comments in source code
  2. API Documentation: Configuration and usage reference
  3. Architecture Documentation: System design and decisions
  4. User Documentation: README, getting started guides

Documentation Standards

  • Keep examples up to date with the current API
  • Include error cases in documentation
  • Use clear, concise language
  • Provide complete examples that work without modification

Updating Documentation

When making changes, update relevant documentation:

  • Rustdoc comments for new or changed APIs
  • API documentation in docs/api.md for configuration changes
  • README.md for new features or usage changes
  • Architecture documentation for design changes

Issue Reporting

Before Reporting

  1. Search existing issues to avoid duplicates
  2. Check the documentation to ensure it's actually a bug
  3. Try the latest version to see if it's already fixed

Bug Reports

Include this information in bug reports:

## Bug Description
A clear description of what the bug is.

## Steps to Reproduce
1. Create config file with...
2. Run caddy-rs with...
3. Send request to...
4. See error

## Expected Behavior
What you expected to happen.

## Actual Behavior
What actually happened.

## Environment
- OS: (e.g., Ubuntu 20.04)
- Rust version: (e.g., 1.75.0)
- Caddy-RS version: (e.g., 0.1.0)

## Configuration
```json
{
  "your": "configuration",
  "file": "here"
}

Logs

Relevant log output here

### Feature Requests

Structure feature requests like this:

```markdown
## Feature Description
A clear description of the feature you'd like to see.

## Use Case
Describe the problem this feature would solve.

## Proposed Solution
Your ideas for how this could be implemented.

## Alternatives
Other ways this problem could be solved.

## Additional Context
Any other context, screenshots, or examples.

Questions and Support

  • Check the documentation first (README, docs/, etc.)
  • Search existing issues for similar questions
  • Create a new issue with the "question" label
  • Be specific about what you're trying to achieve

License

By contributing to Caddy-RS, you agree that your contributions will be licensed under the same license as the project (Apache License 2.0).

Recognition

Contributors will be recognized in the project README and release notes. Significant contributors may be invited to become project maintainers.

Thank you for contributing to Caddy-RS! 🦀