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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! # web_net
//!
//! The web layer on serve-net: what a site is written in.
//!
//! A site in five lines:
//!
//! ```no_run
//! use web_net::{App, get};
//!
//! App::builder()
//! .route(get("/", |cx| cx.html(200, "<h1>Hello</h1>")))
//! .route(get("/posts/:slug", |cx| cx.text(200, &cx.param("slug")?)))
//! .build()?
//! .listen("127.0.0.1:8080")?
//! .wait();
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! A handler gets a [`Context`] and ends with one reply: [`Context::html`],
//! [`Context::text`], [`Context::bytes`], [`Context::redirect`], or [`Context::render`] for a
//! template. `?` turns any error into the right error page.
//!
//! 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 a front that
//! authenticated already passes the identity to [`App::respond`]).
//! 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. With a gate that
//! has rules, every route declares its access,
//! [`public`](Route::public) or [`allow`](Route::allow), and a route
//! without one stops the build. With [`Open`] (no sign-in) every route is
//! public.
//! 7. **hooks and handler**: registered [`Hook`]s for this route, then the
//! handler, which receives a [`Context`]: 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, get};
//!
//! let app = App::builder()
//! .host("example.com")
//! .alias("www.example.com")
//! .base("/blog")
//! .route(get("/", |cx| cx.html(200, "<h1>Blog</h1>")))
//! .route(get("/posts/:slug", |cx| cx.text(200, &cx.param("slug")?)))
//! .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>(())
//! ```
//!
//! [`App::listen`] serves one app on one address with 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 reference with every public item is `docs/API.md` in the
//! repository.
pub use ;
pub use ;
pub use Cors;
pub use Context;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use MAX_PARAMS;
pub use Sites;
pub use ;
/// cookie-net, at the version web-net uses, for [`Context::set_cookie`].
pub use cookie_net;
/// csrf-sec, at the version web-net uses, for [`Builder::csrf`].
pub use csrf_sec;
/// form-net, at the version web-net uses, for [`Context::form`] and its `form!`
/// macro.
pub use form_net;
/// Crate version string, populated by Cargo at build time.
///
/// ```
/// assert_eq!(web_net::VERSION.split('.').count(), 3);
/// ```
pub const VERSION: &str = env!;
/// The README and `docs/API.md` are compiled and run as doctests, so the
/// reference cannot drift from the API.