1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! # web_net
//!
//! The web layer on serve-net: what a site is written in.
//!
//! A request runs one fixed path, and each step either answers or hands on:
//!
//! 1. **site**: the `Host` picks the site (unknown hosts get 421, aliases a
//! redirect to the canonical host), and its base path is removed, so a
//! site works installed in a folder.
//! 2. **url**: the path is decoded once, dot segments are removed, empty
//! segments collapse, and the trailing-slash policy applies. A path that
//! was not spelled canonically is redirected to the spelling that is, so
//! two spellings never reach different routes. An encoded slash, an
//! encoded backslash, a control character, or bytes that are not UTF-8
//! are refused with 400.
//! 3. **authenticate**: the [`Gate`] says who is asking (or the caller did,
//! with [`App::respond_identified`]).
//! 4. **route**: a trie compiled at build time matches the path (404 when
//! nothing does).
//! 5. **method**: `HEAD` follows `GET`, `OPTIONS` is answered for every
//! route, anything else the route does not take is a 405 with `Allow`.
//! 6. **authorize**: the route's rule, checked by the gate. Every route
//! declares its access, [`public`](Route::public) or
//! [`allow`](Route::allow); a route without one stops the build.
//! 7. **hooks and handler**: registered [`Hook`]s for this route, then the
//! handler, which receives a [`Cx`]: proof the request passed its gate.
//! 8. **render**: a [`Render`] engine writes the page from a typed [`View`],
//! no JSON in between.
//! 9. **errors**: every refusal and error is answered by one function, with
//! the site's error template or a built-in page, and reported through
//! [`Events`].
//!
//! ```
//! use web_net::{App, Fields, Value, get};
//!
//! let app = App::builder()
//! .host("example.com")
//! .alias("www.example.com")
//! .base("/blog")
//! .route(get("/", |cx| { cx.html(200, "<h1>Blog</h1>"); Ok(()) }).public())
//! .route(get("/posts/:slug", |cx| {
//! let slug = cx.param("slug").unwrap_or_default();
//! cx.text(200, &slug);
//! Ok(())
//! }).public())
//! .build()?;
//!
//! use serve_net::testing::respond;
//! let r = respond(&app, b"GET /blog/posts/hello HTTP/1.1\r\nHost: example.com\r\n\r\n");
//! assert_eq!((r.status, r.text()), (200, "hello"));
//!
//! let r = respond(&app, b"GET /blog/./posts//hello/ HTTP/1.1\r\nHost: example.com\r\n\r\n");
//! assert_eq!((r.status, r.header("location")), (301, Some(&b"/blog/posts/hello"[..])));
//!
//! let r = respond(&app, b"GET /blog HTTP/1.1\r\nHost: www.example.com\r\n\r\n");
//! assert_eq!(r.header("location"), Some(&b"http://example.com/blog/"[..]));
//!
//! let r = respond(&app, b"GET / HTTP/1.1\r\nHost: other.example\r\n\r\n");
//! assert_eq!(r.status, 421);
//! # Ok::<(), web_net::BuildError>(())
//! ```
//!
//! On a server: `serve_net::Server::builder()...serve(app)`, or
//! `serve_net::http("127.0.0.1:8080", ...)` for a quick start with a closure.
pub use ;
pub use Cx;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use MAX_PARAMS;
pub use ;
/// Crate version string, populated by Cargo at build time.
pub const VERSION: &str = env!;