Install
[]
= "0.1"
= { = "1", = ["full"] }
Plugins and transports are opt-in features on the umbrella crate:
= { = "0.1", = ["full", "ws", "fs", "tls"] }
| Feature | Pulls in | Gives you |
|---|---|---|
json |
churust-json |
Json<T> extractor/responder, content negotiation |
logging |
churust-logging |
CallLogging via tracing |
cors |
churust-cors |
preflight + CORS headers |
auth |
churust-auth |
Bearer/Basic/JWT, Principal<P> |
full |
all four above | the whole plugin set |
ws |
churust-core/ws |
WebSocket upgrade + WebSocket/Message |
fs |
churust-core/fs |
StaticFiles directory handler |
tls |
churust-core/tls |
rustls-backed HTTPS |
Every Churust crate is released in lockstep on one version number, so
churust-core, churust-json, and the rest always match the umbrella version.
Churust gives you Ktor's developer experience in Rust: an application engine, a
routing DSL, an install(plugin) system, and a phased interceptor pipeline —
built on a battle-tested async stack (tokio + hyper + rustls). Churust owns
the ergonomic layer; it does not reinvent HTTP parsing or TLS.
use *;
async
Features
- Hybrid handlers — write call-style (
|call: Call|) or with typed extractors (Path<T>,Query<T>,State<T>,Json<T>,BearerToken,Principal<P>) — mix freely in one handler. install(plugin)— Ktor-style plugins composed over a named pipeline (Setup → Monitoring → Plugins → Call → Fallback) for deterministic order.- Built-in plugins — JSON content negotiation, request logging, CORS, and authentication (Bearer / Basic / JWT).
- Typed app state / DI —
.state(T)then extractState<T>. - Layered config — defaults <
churust.toml< env (CHURUST_*) < code DSL. - Secure by default — body-size limits, request timeouts, panic isolation (a panicking handler returns 500, never crashes the server), no version banner, opt-in rustls TLS.
- Fast, in-process tests —
TestClientdrives the full pipeline without binding a socket.
Workspace layout
| Crate | What it is |
|---|---|
churust |
Umbrella crate + prelude. Depend on this. Plugins behind features. |
churust-core |
Engine, routing, pipeline, Call, extractors, config, state, TLS, test harness. |
churust-macros |
#[churust::main]. |
churust-json |
Json<T> extractor/responder + ContentNegotiation plugin (feature json). |
churust-logging |
CallLogging plugin via tracing (feature logging). |
churust-cors |
Cors plugin — preflight + headers (feature cors). |
churust-auth |
Auth (Bearer/Basic/JWT) + Principal<P> (feature auth). |
examples/hello |
Minimal server. |
examples/api |
JSON CRUD using all four plugins + auth-gated routes. |
Enable plugins via features (full enables all four; tls enables rustls):
[]
= { = "0.1", = ["full"] } # json, logging, cors, auth
= { = "1", = ["full"] }
A fuller example
use *;
use ;
use Mutex;
async
$ curl localhost:8080/notes
[]
$ curl -X POST localhost:8080/notes -d '{"text":"hi"}'
{"error":"authentication required","status":401}
$ curl -X POST localhost:8080/notes -H 'authorization: Bearer admin-token' \
-H 'content-type: application/json' -d '{"text":"hi"}'
{"id":1,"text":"hi"}
WebSockets (feature ws)
Opt in with features = ["ws"]. A handler takes the WebSocketUpgrade
extractor and calls on_upgrade to switch the request into a bidirectional
socket (a plain GET to the route is rejected with 426 Upgrade Required):
use *;
use ;
r.get;
= { = "0.1", = ["ws"] }
See examples/chat for an echo endpoint plus a broadcast room.
Static files & streaming (feature fs)
Response bodies are a Body — either buffered bytes or a lazy stream — so large
or dynamic payloads never have to be fully materialized in memory. Body is
always available; StaticFiles is behind the opt-in fs feature.
use *; // brings StaticFiles into scope under `fs`
use Body;
r.get;
r.get;
StaticFiles detects the Content-Type from the file extension, serves an
optional index file for directories, rejects path traversal (.., absolute
paths, symlink escapes) with 404, and streams the file in chunks. See
examples/static.
= { = "0.1", = ["fs"] }
Core concepts
Handlers & extractors
A handler is an async closure/fn returning anything that implements
IntoResponse. Arguments are extractors:
FromCallParts(borrow the request) — any position:Path<T>,Query<T>,State<T>,BearerToken,Principal<P>.FromCall(consume the body) — last argument only:Json<T>,Call.
Call itself is the call-style base case (|call: Call| ...).
Pipeline & plugins
A Plugin registers Middleware into a Phase. Middleware own the Call,
may mutate it, call next.run(call), and post-process the Response (the onion
model). Phase order is fixed and deterministic regardless of install order.
Configuration
# churust.toml
[]
= "0.0.0.0"
= 8080
= 1048576
= 30000
[] # requires the `tls` feature
= "cert.pem"
= "key.pem"
Churust::from_config() loads churust.toml + CHURUST_* env vars; chained
DSL setters override. Example: CHURUST_SERVER_PORT=9090.
Testing
use TestClient;
let app = build_app;
let client = new;
let res = client.get.send.await;
assert_eq!;
Development
Status
Churust v1 is feature-complete: routing, hybrid extractors, the four core
plugins, named phases, config, state, timeouts, TLS, and the #[churust::main]
macro. v2.0 adds WebSockets behind the opt-in ws feature (see above).
v2.1 adds streaming response bodies (the always-on Body type) and static
file serving (StaticFiles, behind the opt-in fs feature). Still deferred
(YAGNI): sessions, response compression, HTTP/3, route-scoped middleware sugar.
Design docs and build plans live in docs/.
License
MIT