Skip to main content

web_net/
sites.rs

1//! Several sites in one server: the host and the base path pick the
2//! application.
3
4use serve_net::{Envelope, HttpReply, HttpRequest, HttpService, Outcome};
5
6use crate::{App, Gate, NoRender, Open, Render, Served};
7
8/// A table of applications, chosen per request by host and base path: an
9/// application that lists the request's host (or an alias of it) wins over
10/// one that answers any host, and among those, the longest base path that
11/// holds the request's path. A request no application claims goes to the
12/// first one added, which answers it by its own rules (421 for a host it
13/// does not serve, 404 outside its base).
14///
15/// ```
16/// use web_net::{App, Sites, get};
17///
18/// let blog = App::builder()
19///     .host("example.com")
20///     .base("/blog")
21///     .route(get("/", |cx| cx.text(200, "blog")))
22///     .build()?;
23/// let shop = App::builder()
24///     .host("shop.example.com")
25///     .route(get("/", |cx| cx.text(200, "shop")))
26///     .build()?;
27/// let main = App::builder()
28///     .host("example.com")
29///     .route(get("/", |cx| cx.text(200, "home")))
30///     .build()?;
31/// let sites = Sites::new().site(main).site(blog).site(shop);
32///
33/// use serve_net::testing::respond;
34/// assert_eq!(respond(&sites, b"GET /blog/ HTTP/1.1\r\nHost: example.com\r\n\r\n").text(), "blog");
35/// assert_eq!(respond(&sites, b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n").text(), "home");
36/// assert_eq!(respond(&sites, b"GET / HTTP/1.1\r\nHost: shop.example.com\r\n\r\n").text(), "shop");
37/// assert_eq!(respond(&sites, b"GET / HTTP/1.1\r\nHost: other.example\r\n\r\n").status, 421);
38/// # Ok::<(), web_net::BuildError>(())
39/// ```
40pub struct Sites<G: Gate = Open, E: Render = NoRender> {
41    apps: Vec<App<G, E>>,
42}
43
44impl<G: Gate, E: Render> Default for Sites<G, E> {
45    fn default() -> Self {
46        Self { apps: Vec::new() }
47    }
48}
49
50impl<G: Gate, E: Render> Sites<G, E> {
51    /// An empty table.
52    ///
53    /// ```
54    /// use web_net::Sites;
55    ///
56    /// // With no application, every request is answered with 421.
57    /// let sites: Sites = Sites::new();
58    /// let r = serve_net::testing::respond(&sites, b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
59    /// assert_eq!(r.status, 421);
60    /// ```
61    #[must_use]
62    pub fn new() -> Self {
63        Self::default()
64    }
65
66    /// Adds an application. The first one added answers what no other
67    /// claims.
68    ///
69    /// ```
70    /// use serve_net::testing::respond;
71    /// use web_net::{App, Sites, get};
72    ///
73    /// let main = App::builder().host("example.com").route(get("/", |cx| cx.text(200, "home"))).build()?;
74    /// let shop = App::builder().host("shop.example.com").route(get("/", |cx| cx.text(200, "shop"))).build()?;
75    /// let sites = Sites::new().site(main).site(shop);
76    /// assert_eq!(respond(&sites, b"GET / HTTP/1.1\r\nHost: shop.example.com\r\n\r\n").text(), "shop");
77    /// assert_eq!(respond(&sites, b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n").text(), "home");
78    /// # Ok::<(), web_net::BuildError>(())
79    /// ```
80    #[must_use]
81    pub fn site(mut self, app: App<G, E>) -> Self {
82        self.apps.push(app);
83        self
84    }
85
86    /// The application for a request, if the table has any.
87    ///
88    /// ```
89    /// use serve_net::{Envelope, HttpReply, HttpRequest, HttpService, Outcome};
90    /// use web_net::{App, Sites, get};
91    ///
92    /// // A front that picks the application itself before it answers.
93    /// struct Front(Sites);
94    /// impl HttpService for Front {
95    ///     fn call(&self, req: &HttpRequest<'_>, env: &Envelope, reply: &mut HttpReply) -> Outcome {
96    ///         let app = self.0.pick(req).expect("one site is configured");
97    ///         app.respond(req, env, None, reply).outcome
98    ///     }
99    /// }
100    ///
101    /// let shop = App::builder().host("shop.example.com").route(get("/", |cx| cx.text(200, "shop"))).build()?;
102    /// let front = Front(Sites::new().site(shop));
103    /// let r = serve_net::testing::respond(&front, b"GET / HTTP/1.1\r\nHost: shop.example.com\r\n\r\n");
104    /// assert_eq!(r.text(), "shop");
105    /// # Ok::<(), web_net::BuildError>(())
106    /// ```
107    #[must_use]
108    pub fn pick(&self, req: &HttpRequest<'_>) -> Option<&App<G, E>> {
109        let authority = req.head().authority();
110        let path = req.path();
111        self.apps
112            .iter()
113            .filter_map(|a| a.claim(authority, path).map(|score| (score, a)))
114            .max_by_key(|(score, _)| *score)
115            .map(|(_, a)| a)
116            .or_else(|| self.apps.first())
117    }
118
119    /// Serves every site on `addr`, as [`App::listen`] does for one.
120    ///
121    /// # Errors
122    ///
123    /// As [`App::listen`].
124    ///
125    /// ```no_run
126    /// use web_net::{App, Sites, get};
127    ///
128    /// let main = App::builder().host("example.com").route(get("/", |cx| cx.text(200, "home"))).build()?;
129    /// let shop = App::builder().host("shop.example.com").route(get("/", |cx| cx.text(200, "shop"))).build()?;
130    /// Sites::new().site(main).site(shop).listen("127.0.0.1:8080")?.wait();
131    /// # Ok::<(), Box<dyn std::error::Error>>(())
132    /// ```
133    pub fn listen(
134        self,
135        addr: impl std::net::ToSocketAddrs,
136    ) -> std::result::Result<serve_net::Running, serve_net::ServeError> {
137        crate::app::listen(addr, self)
138    }
139
140    /// As [`App::respond`] on the chosen application.
141    ///
142    /// ```
143    /// use serve_net::{Envelope, HttpReply, HttpRequest, HttpService, Outcome};
144    /// use web_net::{App, Guest, Sites, get};
145    ///
146    /// // A front that authenticated every request already.
147    /// struct Front(Sites);
148    /// impl HttpService for Front {
149    ///     fn call(&self, req: &HttpRequest<'_>, env: &Envelope, reply: &mut HttpReply) -> Outcome {
150    ///         self.0.respond(req, env, Some(&Guest), reply).outcome
151    ///     }
152    /// }
153    ///
154    /// let app = App::builder().route(get("/", |cx| cx.text(200, "hi"))).build()?;
155    /// let r = serve_net::testing::respond(&Front(Sites::new().site(app)), b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
156    /// assert_eq!(r.text(), "hi");
157    /// # Ok::<(), web_net::BuildError>(())
158    /// ```
159    pub fn respond(
160        &self,
161        req: &HttpRequest<'_>,
162        env: &Envelope,
163        who: Option<&G::Identity>,
164        reply: &mut HttpReply,
165    ) -> Served {
166        match self.pick(req) {
167            Some(app) => app.respond(req, env, who, reply),
168            None => {
169                reply.text(421, "no site is configured");
170                Served {
171                    outcome: Outcome::Done,
172                    route: None,
173                    cache: crate::Cacheable::No,
174                }
175            }
176        }
177    }
178}
179
180impl<G: Gate, E: Render> HttpService for Sites<G, E> {
181    fn call(&self, request: &HttpRequest<'_>, env: &Envelope, reply: &mut HttpReply) -> Outcome {
182        self.respond(request, env, None, reply).outcome
183    }
184
185    fn done(&self, env: &Envelope, _finished: &serve_net::Finished) {
186        for app in &self.apps {
187            app.forget(env);
188        }
189    }
190}
191
192impl<G: Gate, E: Render> std::fmt::Debug for Sites<G, E> {
193    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194        f.debug_struct("Sites").field("apps", &self.apps).finish()
195    }
196}