# All Features

> A complete checklist of rust-web-server capabilities organized by category, with feature flag annotations.

Source: https://rws8.tech/docs/getting-started/features/ · Last updated 2026-11-15

This page is a single-page reference for everything the crate ships. Items that require an opt-in feature flag are annotated with the flag name in backticks.

---

## Server / Protocol

- **HTTP/1.1** — keep-alive, chunked transfer encoding, gzip compression, ETags, range requests, 30-second idle read timeout, 30 s to receive a whole request head (`408`)
- **HTTP/2** — ALPN negotiation, full multiplexing, forbidden-header stripping, `Alt-Svc` advertisement — `http2`
- **HTTP/3 / QUIC** — UDP listener via `quinn`, `h3` + `h3-quinn`, QUIC connection-level SNI — `http3` *(default)*
- **TLS** — `rustls` with `aws-lc-rs` crypto backend; no system OpenSSL dependency; FIPS-compatible — `http2`
- **mTLS** — client certificate verification via `WebPkiClientVerifier`; set `RWS_CONFIG_TLS_CLIENT_CA_FILE` — `http2`
- **Virtual-host routing** — per-domain TLS certificates via `SniCertResolver`; `.with_host("example.com")` on any `Router` — `http2`
- **HTTP → HTTPS redirect** — `Server::run_redirect()` listens on a second port and issues `301` responses — `http2`
- **ACME / automatic TLS** — automatic certificate provisioning compatible with Let's Encrypt — `acme`
- **Hot reload** — `SIGHUP` (or `POST /admin/config/reload`) reloads routes, upstreams, health checks, CORS rules, rate limits, log format and TLS certs without restarting — `http2`
- **Graceful shutdown** — `SIGINT` / `SIGTERM` drain in-flight requests before exit; `SERVER_READY` flag cleared on shutdown
- **Gzip compression** — applied automatically based on `Accept-Encoding` to text bodies of at least 512 bytes (`compression::MIN_COMPRESS_SIZE`)
- **Static file serving** — a directory with no `index.html` renders a dark/light-adaptive directory listing page by default; files over 256 KiB streamed (`sendfile(2)` on plain TCP); ETag + `Last-Modified` with `304` revalidation, `Cache-Control: no-cache` by default (`RWS_CONFIG_STATIC_CACHE_CONTROL`)
- **SPA fallback** — `RWS_CONFIG_SPA_FALLBACK=index.html` serves a fallback file for unmatched routes instead of `404`, for React Router/Vue Router/etc. deep links; opt-in, scoped by an extension heuristic and `RWS_CONFIG_SPA_FALLBACK_EXCLUDE_PREFIXES`
- **Thread pool** (http1 only) — hand-rolled `ThreadPool`; connection-per-thread; no tokio — `http1`
- **Tokio async runtime** — used for `http2` and `http3` builds; `http1` is fully synchronous
- **Request framing** — heads up to 64 KiB across any number of reads (`431` beyond), pipelining, chunked request bodies; `Content-Length` + `Transfer-Encoding` or conflicting lengths are rejected (`400`) so a proxy in front can't be desynced (request smuggling)
- **`Expect: 100-continue`** — `100 Continue` is sent before the body is read, after the body-size check
- **Max request body size** — `RWS_CONFIG_MAX_BODY_SIZE_IN_BYTES` rejects oversized bodies with `413` before buffering, on HTTP/1.1, HTTP/2 and HTTP/3
- **Outbound HTTP client** — `Client` (sync) and `AsyncClient` (`http2`); HTTPS with `http-client` or `http2`; redirects, `.form()`, `.body_json()`
- **WebSocket** (RFC 6455) — handshake and frame codec built in
- **WASM guest component** — `rws-wasm-shim` runs `App`/`Router`/middleware inside a `wasi:http/proxy` component (Wasmtime, Spin, Fastly Compute); see [WASM](/deployment/wasm/)

---

## Routing & App Building

- **`App`** — zero-config entry point; wraps all built-in controllers (static files, CORS, health, metrics, MCP, …)
- **`App::with_state(S)`** — state-aware dynamic router; `Arc<S>` shared across all handlers; builder methods `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`
- **`App::with_async_state(S)`** — same API with `async fn` handlers — `http2`
- **`routes!` macro** — ergonomic route registration: `GET "/path" => handler` — `macros`
- **`Router`** — standalone path-based router with named parameters (`:name`) and trailing wildcards (`*name`); `PathParams::get(name)`
- **`Controller` trait** — low-level `is_matching` / `process` interface; register in `App::execute` for maximum control
- **Typed extractors** — `Body` (raw bytes), `BodyText` (UTF-8), `Query` (parsed query string), `RequestHeaders` (case-insensitive header map)
- **Typed errors** — `AppError` enum (`BadRequest`, `Unauthorized`, `Forbidden`, `NotFound`, `Conflict`, `UnprocessableEntity`, `TooManyRequests`, `Internal`) with `IntoResponse` trait
- **`Middleware` trait** — `handle(request, connection, next) -> Result<Response, String>`; compose via `.wrap(layer)`
- **`WithMiddleware<A>`** — wraps any `Application`; layers applied in push order (first-pushed is outermost)
- **JSON (de)serialization** — `serde` / `serde_json` integration (e.g. `http_client::Response::json::<T>()`) — `serde`
- **Tera templates** — render `.html` templates with a `serde_json::Value` context via `tera::render` — `tera`
- **In-process test client** — `TestClient::new(app)` dispatches requests without opening a TCP socket; use in unit and integration tests
- **WebSocket** — RFC 6455 handshake and frame codec built in; no third-party WebSocket library
- **SSE (Server-Sent Events)** — `Sse` builder for streaming events; ideal for streaming AI model output
- **Background scheduler** — `Scheduler::new().every(duration, fn).cron("…", fn).start()`
- **Background job queue** — `JobQueue` (in-memory) or `PersistentJobQueue` (crash-safe, model-backed); retry with exponential backoff — `jobs`
- **File / object storage** — `Storage` trait; `LocalStorage` (disk), `S3Storage` (AWS S3, R2, MinIO via AWS SigV4, no SDK), or `AzureBlobStorage` (Azure Blob via Shared Key/Managed Identity, no SDK) — `storage-local` / `storage-s3` / `storage-azure`
- **OpenAPI / Swagger** — `.openapi(OpenApiConfig)` generates `GET /openapi.json` + `GET /docs` (Swagger UI) from registered routes — `openapi`
- **Email / SMTP** — `mailer` module, plain SMTP; STARTTLS/SMTPS also needs `http-client` or `http2` — `mailer`
- **Outbound HTTP client** — `http_client::Client` (always compiled for plain HTTP); HTTPS without the server TLS stack — `http-client`
- **Per-route timeouts** — `with_timeout`/`with_timeout_state`/`with_timeout_async` wrap a handler with its own deadline; `TimeoutLayer` for a whole app; config-driven proxy's `timeout_ms`
- **Request ID middleware** — `RequestIdLayer` injects/echoes `X-Request-Id` on every request and response; `RequestId` extractor to read it

---

## Proxy & Gateway

- **`ReverseProxy`** — HTTP/1.1 reverse proxy middleware; round-robin backend selection
- **`H2ReverseProxy`** — HTTP/2 upstream proxy; pooled, multiplexed upstream connections on a dedicated runtime (works under any tokio runtime flavor) — `http2`
- **`GrpcProxy`** — wraps `H2ReverseProxy`; filters on `Content-Type: application/grpc*` — `http2`
- **`TcpProxy`** — standalone L4 TCP proxy; bidirectional `std::io::copy` relay; round-robin backends
- **`UdpProxy`** — standalone UDP datagram proxy; per-datagram ephemeral socket; round-robin backends
- **`WsProxy`** — standalone WebSocket proxy; relays raw bytes after upgrade handshake
- **Config-driven proxy mode** — `rws.config.toml` with `[[upstream]]` + `[[route]]` sections; no code required
- **Health checking** — per-upstream background health checker; dead backends removed automatically; configurable thresholds and intervals
- **Load balancing** — `round_robin`, `random`, `ip_hash`, or `least_connections` across live backends
- **`RewriteLayer`** — rewrite request headers, URI, and response headers / body / status code per route; regex URI rewrites via `request_uri_regex_rewrite` — `rewrite-regex`
- **Per-route rate limiting** — `PerRouteRateLimit` in config-driven mode; `RateLimitLayer` in code
- **Per-route Bearer auth** — `BearerAuthMiddleware` in config-driven mode; `JwtLayer` in code
- **Route matching** — host, path prefix, exact path, HTTP method, `Content-Type` prefix; first-match wins
- **Service discovery** — `BackendPool` with `Static`/`EnvPrefix`/`File`/`Dns`/`DnsSrv` (weighted)/`Consul`/`Docker` (label value as address) sources on a poll loop, plus `EtcdWatch` on a dedicated live watch stream
- **Circuit breaker + retries** — `CircuitBreaker` (Closed → Open → HalfOpen, capped probes), `RedisCircuitBreaker` (state shared across instances), `RetryLayer` on 502/503/504; `ReverseProxy::with_circuit_breaker`; `rws_circuit_breaker_state` metric
- **Canary / traffic splitting** — `CanaryLayer` with smooth weighted round-robin, live weight updates, `BackendPool` groups
- **Kubernetes Ingress** — `KubernetesIngressWatcher` watches the API (Exact/Prefix `pathType`, `ingressClassName`), `.from_service_account()` in-cluster
- **Private-CA upstreams** — `[[upstream]] tls_ca_file` / `ReverseProxy::tls_ca_file`
- **Non-blocking proxying** — config routes without middleware forward on the event loop, holding no thread

---

## Security

- **`RateLimitLayer`** — sliding-window rate limiter keyed by client IP; global singleton via `rate_limit::global()`; returns `429` when budget exceeded
- **`RedisRateLimiter`** — fixed-window limiter in Redis for a budget shared across instances
- **`BasicAuthLayer`** — HTTP Basic authentication middleware — `auth`
- **`JwtLayer`** — JWT Bearer token validation middleware (HS256) — `auth`; RS256/ES256 against a static public key via `::rs256`/`::es256` — `auth-asymmetric`
- **`ForwardAuthLayer`** — delegates the allow/deny decision to an external HTTP service (Traefik/nginx `auth_request` style) — `auth`
- **`IpFilter`** — allowlist / denylist middleware keyed by client IP
- **`crypto`** — Argon2id password hashing and verification; `signed_cookie`/`verify_signed_cookie` (HMAC-SHA256) and `encrypted_cookie`/`decrypt_cookie` (AES-256-GCM) — `crypto`
- **CSRF tokens** — secure random token generation and validation — `csrf`
- **SSO / OAuth 2.0 / OIDC** — `OidcAuth` login flow, RS256/ES256 `id_token` verification via JWKS, outbound HTTPS token exchange — `sso`
- **OAuth 2.0 Authorization Server** — `AuthServer`: rws issues its own HS256 tokens (`/oauth/token`, `/oauth/authorize`) — `sso-server`
- **SAML 2.0 Service Provider** — `SamlSp`: metadata, login, ACS, logout — `sso-saml`
- **Webhook verification** — `verify_webhook_signature` for GitHub, Shopify, Stripe — `webhook`
- **Secrets manager references** — `secrets::resolve()` for `vault://`, `aws-sm://`, `azkv://` — `secrets`
- **CORS** — built-in CORS controller; configurable origins, methods, and headers; hot-reloadable
- **mTLS** — mutual TLS client certificate verification — `http2`
- **No OpenSSL** — TLS via `rustls` + `aws-lc-rs`; fully static binaries

---

## Observability & Ops

- **`/healthz`** — liveness probe; always `200 OK` while the server is up
- **`/readyz`** — readiness probe; returns `503` after graceful shutdown begins (`SERVER_READY` flag)
- **`/metrics`** — Prometheus text-format scrape endpoint
- **`MetricsLayer`** — per-route `rws_route_requests_total{method,path,status}` counters and `rws_route_duration_seconds{method,path}` histograms
- **Global metrics** — `record_request()`, `record_error()`, `connection_open()` / `connection_close()` counters
- **`OtelLayer`** — OpenTelemetry trace propagation middleware
- **`CacheLayer`** — response caching middleware
- **Structured logging** — `RWS_CONFIG_LOG_FORMAT=json` (default), `combined`, or `off`; hot-reloadable
- **Config hot reload** — `SIGHUP` or `POST /admin/config/reload` reloads routes, upstreams, health checks, CORS, rate limits, log format and TLS certs; no restart required
- **Kubernetes-ready** — `/healthz`, `/readyz`, `/metrics` all built in; graceful `SIGTERM` drain; static binary with no system dependencies
- **`rws --check`** — validates `rws.config.toml` (unknown keys, bad values, TLS files, undeclared upstreams, unset auth secrets) and exits 1 on errors, like `nginx -t`; `--format=json`
- **Helm chart** — `helm/rws/`
- **Background scheduler** — fixed-rate, fixed-delay and 6-field cron tasks
- **Job queue** — `JobQueue` (in-memory) and `PersistentJobQueue` (model-backed); retries with exponential backoff — `jobs`
- **Email** — `Mailer` over SMTP, STARTTLS or SMTPS; text + HTML — `mailer`

---

## AI & MCP

- **`McpServer`** — implements `Application`; serves the MCP Streamable HTTP protocol (`POST /mcp`, JSON-RPC 2.0)
- **Tool registration** — `.tool(name, description, schema, handler)` — any function returning a string
- **Resource registration** — `.resource(uri_template, name, description, handler)`
- **Prompt registration** — `.prompt(name, description, handler)`
- **Bearer auth gate** — `.require_bearer(token)` gates all MCP requests behind a static token
- **Fallthrough** — `.wrap(app)` passes non-MCP requests to another `Application`
- **20 built-in rws tools** in the binary — config, metrics, routes, cache, rate limits, CORS, static files, reload, maintenance mode, IP blocklist, feature flags, recent requests/errors (see [MCP](/mcp/overview/))
- **SSE streaming** — `Sse` builder for streaming AI tokens to browser clients
- **Protocol coverage** — session context (`.tool_with_context`), tool annotations, image/embedded content, JSON-RPC batches, list pagination, `GET /mcp` SSE push, `logging/setLevel`, dynamic registration with `list_changed`, progress, `completion/complete`, cancellation, `resources/subscribe`, server-side sampling, `roots/list`, async tools (`http2`), OAuth (`.require_oauth`, `sso`)

---

## Database / ORM

All ORM features require at least one of `model-sqlite`, `model-postgres`, or `model-mysql` (async, backed by `sqlx`; several can be compiled in, pick one with `RWS_DB_BACKEND`).

- **`#[derive(Model)]`** — generates `impl Model`, `Struct::repository()`, `Struct::query()` — `macros`
- **Table and column mapping** — `#[table(name = "…")]`, `#[column(name = "…")]`, `#[primary_key]`, `#[primary_key(auto_increment)]`, `#[ignore]`
- **`DbPool`** — async `sqlx` pool: `execute`, `query_rows`, `query::<T>`, `query_raw`, `begin` (→ `DbTransaction` with `commit`/`rollback`), `transaction(closure)`, `migrate`
- **`Repository<T, ID>`** — `save` (INSERT or UPDATE by primary key), `save_all`, `find_by_id`, `find_all`, `delete_by_id`, `delete_all_by_id`, `count`, `exists_by_id`
- **`QueryBuilder`** — fluent `where_eq`, `filter`, `order_by`, `limit`, `offset`, `paginate`, `fetch_all`, `fetch_one`, `count`, `delete`, `update`; placeholder tokens auto-converted to `?` (SQLite/MySQL) or `$N` (PostgreSQL)
- **Migrations** — reads `*.sql` files in lexicographic order; `_schema_migrations` table tracks applied versions; each migration wrapped in a transaction
- **Relations** — `HasMany<T>`, `HasOne<O>`, `BelongsTo<O>` with explicit `.load(&pool)`; no lazy loading, no hidden N+1 queries
- **SQLite** — `sqlx` SQLite driver — `model-sqlite`
- **PostgreSQL** — `sqlx` Postgres driver — `model-postgres`
- **MySQL / MariaDB** — `sqlx` MySQL driver — `model-mysql`

---

## Sessions & Cookies

- **`CookieJar` / `SetCookie`** — parse request cookies, write every RFC 6265 attribute
- **Sessions** — `SessionStore` (in-memory TTL), `DbSessionStore` (model-backed), `RedisSessionStore`

---

## File / Object Storage

- **`Storage` trait** — `put` / `get` / `delete` / `url`; swap backends without touching handlers
- **`LocalStorage`** — local disk — `storage-local`
- **`S3Storage`** — AWS S3, Cloudflare R2, MinIO via SigV4; static keys or EKS IRSA / ECS task role / EC2 IMDSv2; no AWS SDK — `storage-s3`
- **`AzureBlobStorage`** — Shared Key or Managed Identity; no Azure SDK — `storage-azure`

---

## Secrets

- **`secrets::resolve`** — `vault://path#field`, `aws-sm://name[#field]`, `azkv://vault/secret` resolved over HTTPS — `secrets`
- **Any `RWS_CONFIG_*` can be a reference** — `resolve_env_vars()` runs in `Server::setup()`; a broken reference fails startup instead of running with the literal string

---

## Dependency Injection

- **`Container`** — `TypeId`-keyed service store for concrete types and `dyn Trait` objects
- **`register::<T>(value)`** — wraps in `Arc<T>`, keyed by `TypeId::of::<T>()`
- **`provide::<T: ?Sized>(Arc<T>)`** — stores trait objects directly
- **Named services** — `get_named::<T>(name)` / `register_named::<T>(name, value)` for multiple instances of the same type
- **`App::with_state(container)`** — pass the container directly as state; it's `Send + Sync + 'static` like any other state type, no wrapping needed
- **`into_arc()`** — for sharing one container across multiple hand-built `Application`s outside of `with_state`
- No external dependencies
