pollux/AGENTS.md
Jeena 0468781a69 Add configurable global concurrent request limiting
- Add max_concurrent_requests config option (default: 1000)
- Implement global AtomicUsize counter for concurrent request tracking
- Return status 41 'Server unavailable' when limit exceeded
- Proper counter management with decrements on all exit paths
- Add comprehensive config validation (1-1,000,000 range)
- Update documentation with rate limiting details
- Add unit tests for config parsing
- Thread-safe implementation using Ordering::Relaxed

This provides effective DDoS protection by limiting concurrent
connections to prevent server overload while maintaining
configurability for different deployment scenarios.
2026-01-16 02:26:59 +00:00

5.3 KiB

Overview

This project is a very simple gemini server which only serves static files, nothing else. It is meant to be generic so other people can use it.

Build/Test/Lint Commands

Core Commands

  • cargo build - Build the project
  • cargo build --release - Build optimized release version
  • cargo run - Run the server with default config
  • cargo test - Run all unit tests
  • cargo test <test_name> - Run a specific test
  • cargo test <module>::tests - Run tests in a specific module
  • cargo clippy - Run linter checks for code quality
  • cargo clippy --fix - Automatically fix clippy suggestions
  • cargo clippy --bin <name> - Check specific binary
  • cargo fmt - Format code according to Rust standards
  • cargo check - Quick compile check without building

Common Test Patterns

  • cargo test config::tests - Run config module tests
  • cargo test request::tests - Run request handling tests
  • cargo test -- --nocapture - Show println output in tests

Code Style Guidelines

Imports

  • Group imports: std libs first, then external crates, then local modules
  • Use use crate::module::function for internal imports
  • Prefer specific imports over use std::prelude::*
  • Keep imports at module level, not inside functions

Code Structure

  • Use #[tokio::main] for async main function
  • Keep functions small and focused (single responsibility)
  • Use const for configuration values that don't change
  • Error handling with Result<T, E> and ? operator
  • Use tracing for logging, not println! in production code

Naming Conventions

  • PascalCase for types, structs, enums
  • snake_case for functions, variables, modules
  • SCREAMING_SNAKE_CASE for constants
  • Use descriptive names that indicate purpose

Error Handling

  • Use io::Result<()> for I/O operations
  • Convert errors to appropriate types with map_err when needed
  • Use unwrap() only in tests and main() for unrecoverable errors
  • Use expect() with meaningful messages for debugging
  • Return early with Err() for validation failures

Security Requirements

  • Critical: Always validate file paths with path_security::validate_path
  • Never construct paths from user input without validation
  • Use timeouts for network operations (tokio::time::timeout)
  • Limit request sizes (see MAX_REQUEST_SIZE constant)
  • Validate TLS certificates properly
  • Never expose directory listings

Testing Guidelines

  • Use tempfile::TempDir for temporary directories in tests
  • Test both success and error paths
  • Use #[cfg(test)] for test modules
  • Create temporary test files in tmp/ directory
  • Test security boundaries (path traversal, invalid inputs)
  • Use assert_eq! and assert! for validations

Lint Checking

  • cargo clippy - Run linter checks for code quality
  • cargo clippy --fix - Automatically fix clippy suggestions
  • cargo clippy --bin <name> - Check specific binary
  • cargo fmt - Format code to match Rust standards
  • Run clippy before every commit - Address all warnings before pushing code
  • Current clippy warnings (2025-01-15):
    • src/server.rs:16-17 - Unnecessary borrows on file_path
    • src/logging.rs:31 - Match could be simplified to let statement

Async Patterns

  • Use .await on async calls
  • Prefer tokio::fs over std::fs in async contexts
  • Handle timeouts for network operations
  • Use Arc<Clone> for shared data across tasks

Gemini Protocol Specific

  • Response format: "STATUS META\r\n"
  • Status 20: Success (follow with MIME type)
  • Status 41: Server unavailable (timeout, overload)
  • Status 51: Not found (resource doesn't exist)
  • Status 59: Bad request (malformed URL, protocol violation)
  • Default MIME: "text/gemini" for .gmi files
  • Default file: "index.gmi" for directory requests

Error Handling

  • Concurrent request limit exceeded: Return status 41 "Server unavailable"
  • Timeout: Return status 41 "Server unavailable" (not 59)
  • Request too large: Return status 59 "Bad request"
  • Empty request: Return status 59 "Bad request"
  • Invalid URL format: Return status 59 "Bad request"
  • Hostname mismatch: Return status 59 "Bad request"
  • Path resolution failure: Return status 51 "Not found" (including security violations)
  • File not found: Return status 51 "Not found"
  • Reject requests > 1024 bytes (per Gemini spec)
  • Reject requests without proper \r\n termination
  • Use tokio::time::timeout() for request timeout handling
  • Configurable concurrent request limit: max_concurrent_requests (default: 1000)

Configuration

  • TOML config files with serde::Deserialize
  • CLI args override config file values
  • Required fields: root, cert, key, host
  • Optional: port, log_level, max_concurrent_requests

Development Notes

  • Generate self-signed certificates for local testing in tmp/ directory
  • Use CN=localhost for development
  • Fix every compiler warning before committing any code
  • Create temporary files in the tmp/ directory for your tests like .gem files or images, etc., so they are gitignored
  • Use path-security crate for path validation
  • Default port: 1965 (standard Gemini port)
  • Default host: 0.0.0.0 for listening
  • Log level defaults to "info"

Environment Setup

  • Install clippy: rustup component add clippy
  • Ensure ~/.cargo/bin is in PATH (add source "$HOME/.cargo/env" to ~/.bashrc)
  • Verify setup: cargo clippy --version