exchange 0.1.0

A minimal, configuration-driven HTTP reverse proxy built with hyper and tokio.
exchange-0.1.0 is not a library.

↔️ Exchange

A minimal, configuration-driven reverse proxy in Rust, built with hyper, reqwest, and tokio.

It reads a proxy.toml file at startup to configure routes and server settings, forwarding incoming requests to the appropriate upstream services.


🚀 Features

  • Configuration-driven: All routes and settings are managed in a simple proxy.toml file.
  • Path-based Routing: Forward requests based on the incoming request path.
  • Wildcard Matching: Use wildcard = true to match all sub-paths of a given pattern.
  • Connection Pooling: Uses reqwest's connection pool for efficient upstream connections.
  • Configurable Limits: Set maximum request body size and gateway timeouts.

🔧 Configuration

Create a proxy.toml file in the same directory where you run the application.

Example proxy.toml

# The port the proxy server will listen on
port = 8080

# --- Optional Settings ---

# Max request body size in bytes (default: 1048576, or 1MB)
max_body_bytes = 2097152 # 2MB

# Upstream request timeout (default: 5s)
# Uses serde's Duration format
gateway_timeout = { secs = 10, nanos = 0 }

# --- Routes ---

# An exact-match route
# Requests to "http://localhost:8080/api/users"
# will be proxied to "http://users-service:3000/api/users"
[[routes]]
pattern = "/api/users"
target = "http://users-service:3000"
# wildcard = false (this is the default)

# A wildcard route
# Requests to "http://localhost:8080/assets/main.css"
# will be proxied to "http://static-files:9000/assets/main.css"
[[routes]]
pattern = "/assets"
target = "http://static-files:9000"
wildcard = true

Configuration Details

  • port (u16, Required): The port for the proxy to listen on.
  • max_body_bytes (u64, Optional): The maximum allowed request body size in bytes.
    • Default: 1048576 (1MB)
  • gateway_timeout (Duration, Optional): The time to wait for a response from the upstream service.
    • Default: { secs = 5, nanos = 0 } (5 seconds)
  • connection_pool_size (usize, Optional): Max idle connections per upstream host.
    • Default: 2 * (number of system cores)
  • [[routes]] (Array, Required): A list of route objects.
    • pattern (string): The incoming URI path to match.
    • target (string): The base URI of the upstream service to forward to.
    • wildcard (bool, Optional): If true, matches any path that starts with the pattern. If false (default), requires an exact path match.

🛠️ How It Works

Exchange listens for incoming requests and matches them against its list of routes.

  1. A Client sends a request (e.g., GET /assets/img.png).
  2. Exchange receives the request on port.
  3. It searches its routes for a match.
    • It finds the [[routes]] entry with pattern = "/assets" and wildcard = true.
  4. It constructs the full target URL: http://static-files:9000 (target) + /assets/img.png (original path).
  5. It forwards the request, headers, and body to the Upstream Service.
  6. The service's response is streamed back to the original Client.

If no route matches, a 404 Not Found is returned. If the incoming request body exceeds max_body_bytes, a 413 Payload Too Large is returned.


📦 Running the Proxy

  1. Ensure you have Rust and Cargo installed.

  2. Create your proxy.toml file in the project's root directory.

  3. Run the server in release mode:

    cargo run --release
    
  4. The proxy will start on the port specified in your config file.

    Proxy server running on http://0.0.0.0:8080
    Press Ctrl+C to stop