Quick start
[]
= "0.10"
use ;
A handler ends with one reply: cx.html, cx.text, cx.bytes, cx.redirect, or cx.render for a template. ? turns any error into the right error page.
With no host declared, the site answers localhost (and 127.0.0.1, [::1], *.localhost) only, so it works on a laptop as it is and a name someone else points at the server is never the site. Declare the real host before deploying (.host("example.com")), or .any_host() behind a front that checked it.
A site with a host, a folder, and a form:
use ;
App::listen uses serve-net's defaults. For TLS, several listeners, or other limits, an App is a serve-net HttpService: serve_net::Server::builder()...serve(app).
The request path
| Step | What happens | Answers |
|---|---|---|
| site | the host picks the site; the base path is removed | 421 unknown host; 301 alias or HTTPS |
| url | decoded once, one canonical spelling, dot and empty segments removed | 400 refused; 301/308 to the canonical path |
| authenticate | the gate says who is asking | the gate's denial |
| route | a trie compiled at build | 404 |
| method | HEAD from GET, OPTIONS automatic |
405 with Allow, 501 |
| authorize | the route's rule, unless it is public | 303 to sign-in, 401, 403, 404 |
| hooks, handler | only the hooks registered for the route | the handler's error |
| render | the engine reads a typed view; no JSON | 500 |
| errors | one function: the engine's error template or a built-in page |
Plugging in
- Authentication and authorization: implement
Gate(authenticate,authorize) andIdentity, thenApp::with_gate(gate). Every route then declares.public()or.allow(rule), or the app does not build; a group's rule holds for every route in it. A front layer that authenticates on its own passes the identity toApp::respondand forwards serve-net'sdoneto the app. - Templates: implement
Render. A typed engine compiles against your own types (type View<'a> = Page<'a>); a dynamic one reads anyView.App::builder().render(engine)(orApp::with_gate(gate).render(engine)). Engines live in their own crates; web-net depends on none. - Detection and logs: implement
Events(every refusal, denial, failure, panic) andCheckpoint(in-flight revocation).
Built in: static files (Assets: validators, ranges, precompressed variants, no escape from the folder), health and readiness paths, strict security headers by default (SecurityHeaders), cookies out through cookie-net, and a 304 fast path for pages (Context::not_modified).
Forms and uploads through form-net (Context::form, Context::upload), CSRF through csrf-sec on every unsafe method (origin policy and a session-bound token; a route without either does not build unless it is exempt with a reason), allowlisted external redirects, and If-Match preconditions.
Negotiation (JSON error replies, Context::accept, Context::accept_language), CORS per route or group, gzip compression, cache policies with a report a cache in front can trust (App::respond), and several sites in one table (Sites).
Performance: the warm request path allocates nothing, route lookup does not grow with the number of routes, and web-net adds well under a microsecond of its own work per request (docs/PERFORMANCE.md).
Security: every threat the plan names has an attack test that must fail, the path rules have property tests, whole requests are fuzzed, and docs/SECURITY.md lists each defence.
examples/site.rs puts it together: sessions, sign-in with CSRF, an upload, static files, and pages rendered by Luminess.
Coming next (see the roadmap): 0.10.x releases until a site has run on web-net in production; 1.0 after that.
Contributing
See dev/DIRECTIVES.md for engineering standards and the definition of done. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.