
✨ 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!
489 lines
12 KiB
Markdown
489 lines
12 KiB
Markdown
# 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](#code-of-conduct)
|
|
- [How to Contribute](#how-to-contribute)
|
|
- [Development Setup](#development-setup)
|
|
- [Making Changes](#making-changes)
|
|
- [Pull Request Process](#pull-request-process)
|
|
- [Coding Standards](#coding-standards)
|
|
- [Testing Guidelines](#testing-guidelines)
|
|
- [Documentation](#documentation)
|
|
- [Issue Reporting](#issue-reporting)
|
|
|
|
## Code of Conduct
|
|
|
|
This project adheres to a code of conduct adapted from the [Contributor Covenant](https://www.contributor-covenant.org/). 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**:
|
|
```bash
|
|
git clone https://github.com/your-username/caddy-rs.git
|
|
cd caddy-rs
|
|
```
|
|
3. **Add upstream remote**:
|
|
```bash
|
|
git remote add upstream https://github.com/original-owner/caddy-rs.git
|
|
```
|
|
4. **Install dependencies and build**:
|
|
```bash
|
|
cargo build
|
|
cargo test
|
|
```
|
|
|
|
### Development Tools
|
|
|
|
Install these tools for better development experience:
|
|
|
|
```bash
|
|
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**:
|
|
```bash
|
|
git checkout -b feature/your-feature-name
|
|
```
|
|
|
|
2. **Make your changes**:
|
|
- Follow the [coding standards](#coding-standards)
|
|
- Write tests for new functionality
|
|
- Update documentation as needed
|
|
|
|
3. **Test your changes**:
|
|
```bash
|
|
cargo test # Run all tests
|
|
cargo clippy -- -D warnings # Check for linting issues
|
|
cargo fmt # Format code
|
|
```
|
|
|
|
4. **Commit your changes**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
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:
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
/// 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
|
|
|
|
```rust
|
|
#[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
|
|
|
|
```rust
|
|
#[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:
|
|
|
|
```markdown
|
|
## 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! 🦀 |