A file-based way to set tuning knobs without recompiling. urus is a library, so it never presumes a config path or reads the environment — the binary hands the text in: - Config::with_toml_str(&str) -> Result<Config, ConfigError>: sparse overlay onto an existing Config (built with the addr the binary chose). Only keys present are applied; durations are integer seconds; unknown keys are a hard error (deny_unknown_fields) so a typo is loud, not a silent no-op. - Scope: the four slowloris knobs (head_timeout_secs, body_timeout_secs, body_burst_bytes, body_stall_timeout_secs). Migrating the rest of Config into the file is a separate, additive job — TomlOverrides just grows. - Feature `config-file = ["dep:serde", "dep:toml"]`; the optional serde dep gains the derive feature. The default build is unchanged (deps + code are all gated). - examples/serve_toml.rs (required-features = ["config-file"]): a `--config PATH` demo with no presumed default location. plain_serve and its env vars are left untouched. Tests (feature-gated): empty keeps defaults, partial overrides only named, full overrides all, unknown key errors, malformed errors. 84 lib with the feature / 79 without; clippy --lib clean both ways; e2e smoke serves 200 from a file and rejects an unknown key loudly.
49 lines
1.8 KiB
Rust
49 lines
1.8 KiB
Rust
//! Serve with an optional TOML config overlay.
|
|
//!
|
|
//! urus is a library and never presumes a config path or reads the
|
|
//! environment for one — the binary decides where the file lives and hands
|
|
//! the text to `Config::with_toml_str`. Here that's a `--config PATH` flag;
|
|
//! with no flag, the compiled defaults are used unchanged.
|
|
//!
|
|
//! Requires the `config-file` feature:
|
|
//! cargo run --example serve_toml --features config-file -- --config urus.toml
|
|
//!
|
|
//! Example urus.toml (all keys optional, sparse override; seconds):
|
|
//! head_timeout_secs = 15
|
|
//! body_timeout_secs = 300
|
|
//! body_burst_bytes = 4096
|
|
//! body_stall_timeout_secs = 20
|
|
|
|
use std::net::SocketAddr;
|
|
|
|
use urus::{serve_with, Config, Conn, Next, Pipeline, Router};
|
|
|
|
fn json_id(conn: Conn, _next: Next) -> Conn {
|
|
let id: u64 = conn.params.get("id").and_then(|s| s.parse().ok()).unwrap_or(0);
|
|
conn.put_status(200)
|
|
.put_header("content-type", "application/json")
|
|
.put_body(format!("{{\"id\":{id}}}"))
|
|
}
|
|
|
|
fn main() {
|
|
let addr: SocketAddr = "0.0.0.0:8080".parse().expect("addr");
|
|
let mut cfg = Config::new(addr);
|
|
|
|
// Minimal flag scan: `--config PATH`. No presumed default location.
|
|
let mut args = std::env::args().skip(1);
|
|
while let Some(arg) = args.next() {
|
|
if arg == "--config" {
|
|
let path = args.next().expect("--config needs a PATH");
|
|
let toml = std::fs::read_to_string(&path)
|
|
.unwrap_or_else(|e| panic!("reading config {path}: {e}"));
|
|
cfg = cfg
|
|
.with_toml_str(&toml)
|
|
.unwrap_or_else(|e| panic!("invalid config {path}: {e}"));
|
|
eprintln!("serve_toml: loaded config from {path}");
|
|
}
|
|
}
|
|
|
|
let pipe = Pipeline::new().plug(Router::new().get("/json/:id", json_id));
|
|
serve_with(cfg, pipe).expect("serve");
|
|
}
|