Foxy 🦊
A minimal, configuration-driven, hyper-extensible Rust HTTP proxy library.
Features
- 🛣️ Powerful Routing: Predicate-based routing with path patterns, HTTP methods, headers, and query matching
- 🔄 Flexible Filters: Pre- and post-processing filters for request/response modification
- ⚙️ Configuration Superpowers: Layered configuration from files and environment variables
- 🌐 Fine-grained Control: Route-specific filter chains for precise request handling
- 🔒 Pluggable Security Chain: configurable, provider-based request authentication with built-in providers
- 🚀 Modern Async Architecture: Built on Tokio and Hyper for high performance
- 📦 Lightweight Dependencies: Minimal external dependencies for core functionality
- 🧩 Highly Extensible: Custom predicates, filters and security providers via simple traits
- 🚢 Docker Support: Official container image for rapid deployment
Quickstart
Run the basic proxy example
Run it in your code
Add Foxy as a dependency to your Cargo.toml file
[]
= "..."
Build an instance and start the server.
use Foxy;
// Create a new Foxy instance with layered configuration
let foxy = loader
.with_env_vars // Environment variables (highest priority)
.with_config_file // File-based config (medium priority)
.with_config_file // Defaults (lowest priority)
.build.await?;
// Start the proxy server and wait for it to complete
foxy.start.await?;
Run with Docker
Prerequisites: Docker 20.10+ installed
The project publishes multi‑arch images to GitHub Container Registry:
Run the proxy, exposing the default port 8080 on your host:
Passing a custom configuration file
- Create (or copy) a
config.*file on your host. - Ensure your configuration binds to address
0.0.0.0 - Mount it into the container and tell Foxy where to find it with the
FOXY_CONFIG_FILEenvironment variable:
Run with docker‑compose
If you prefer docker‑compose, drop the snippet below into docker-compose.yml and run docker compose up -d:
version: "3.9"
services:
foxy:
image: johansteffens/foxy:latest
container_name: foxy
ports:
- "8080:8080"
environment:
# Tell Foxy to load the configuration we mounted
FOXY_CONFIG_FILE: /config/config.json
volumes:
# Mount your custom configuration
- ./config.json:/config/config.json:ro
Tip: When you update
config.json, simply restart the container with
docker-compose restart foxyto pick up the changes.
Core Principles
- Predictable Routing: Predicate-based matching with clear priorities determines how requests are routed
- Configurable Processing: Route-specific and global filters for request/response modification
- Extensibility: Trait-based design enables custom predicates and filters
- Configuration-Driven: All behavior controlled via flexible configuration with sensible defaults
Configuration
Foxy's power comes from its rich configuration system. Here's a brief overview:
For detailed information on all configuration options, see the Configuration Guide.
Configuration Sources
Foxy supports multiple configuration sources with priority order:
// Build a layered configuration
let foxy = loader
.with_env_vars // First priority
.with_config_file // Second priority
.build.await?;
Example: FOXY_SERVER_PORT=8080 → server.port
Enabling security
Add a security_chain with a configured provider to your proxy configuration:
{
"proxy": {
"security_chain": [
{
"type": "oidc",
"config": {
"issuer-uri": "https://id.example.com/.well-known/openid-configuration",
"aud": "my-api",
"bypass-routes": [
{ "methods": ["GET"], "path": "/health" }
]
}
}
]
}
}
That’s it — requests hitting /api/** will be validated against the IDP while /health remains public.
Full configuration examples can be found in the Configuration Guide.
Streaming bodies
- Foxy proxies request and response bodies as streams end‑to‑end to ensure there's no full body buffering in memory.
- Large uploads/downloads back‑pressure correctly.
- Memory usage is bound only by socket buffers.
Detailed timing metrics
- Foxy records and logs three high‑resolution latencies on every call (DEBUG level):
[timing] <METHOD> <PATH> -> <STATUS> | total=<X> upstream=<Y> internal=<Z>
| field | description |
|---|---|
| total | wall‑clock time from first byte in to last byte out |
| upstream | time spent awaiting the target server |
| internal | proxy‑side routing / filtering / logging (total − upstream) |
Request and Response body logging
LoggingFilterpeeks and logs the first 1 000 bytes/characters of every request and response body (UTF‑8‑lossy).- Binary or very large payloads are safe—the remainder of the stream is forwarded untouched.
- Please note: enabling request and response logging will introduce additional latency to your calls.
Development Status
- Configuration System
- Loader Module
- Core HTTP Proxy
- Predicate-based Routing
- Request/Response Filters
- Security Chain
- OIDC provider
- Basic auth provider
License
This project is licensed under Mozilla Public License Version 2.0