tide/server.rs
1//! An HTTP server
2
3use async_std::io;
4use async_std::sync::Arc;
5
6#[cfg(feature = "cookies")]
7use crate::cookies;
8use crate::listener::{Listener, ToListener};
9use crate::log;
10use crate::middleware::{Middleware, Next};
11use crate::router::{Router, Selection};
12use crate::{Endpoint, Request, Route};
13
14/// An HTTP server.
15///
16/// Servers are built up as a combination of *state*, *endpoints* and *middleware*:
17///
18/// - Server state is user-defined, and is provided via the [`Server::with_state`] function. The
19/// state is available as a shared reference to all app endpoints.
20///
21/// - Endpoints provide the actual application-level code corresponding to
22/// particular URLs. The [`Server::at`] method creates a new *route* (using
23/// standard router syntax), which can then be used to register endpoints
24/// for particular HTTP request types.
25///
26/// - Middleware extends the base Tide framework with additional request or
27/// response processing, such as compression, default headers, or logging. To
28/// add middleware to an app, use the [`Server::middleware`] method.
29pub struct Server<State> {
30 router: Arc<Router<State>>,
31 state: State,
32 /// Holds the middleware stack.
33 ///
34 /// Note(Fishrock123): We do actually want this structure.
35 /// The outer Arc allows us to clone in .respond() without cloning the array.
36 /// The Vec allows us to add middleware at runtime.
37 /// The inner Arc-s allow MiddlewareEndpoint-s to be cloned internally.
38 /// We don't use a Mutex around the Vec here because adding a middleware during execution should be an error.
39 #[allow(clippy::rc_buffer)]
40 middleware: Arc<Vec<Arc<dyn Middleware<State>>>>,
41}
42
43impl Server<()> {
44 /// Create a new Tide server.
45 ///
46 /// # Examples
47 ///
48 /// ```no_run
49 /// # use async_std::task::block_on;
50 /// # fn main() -> Result<(), std::io::Error> { block_on(async {
51 /// #
52 /// let mut app = tide::new();
53 /// app.at("/").get(|_| async { Ok("Hello, world!") });
54 /// app.listen("127.0.0.1:8080").await?;
55 /// #
56 /// # Ok(()) }) }
57 /// ```
58 #[must_use]
59 pub fn new() -> Self {
60 Self::with_state(())
61 }
62}
63
64impl Default for Server<()> {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl<State> Server<State>
71where
72 State: Clone + Send + Sync + 'static,
73{
74 /// Create a new Tide server with shared application scoped state.
75 ///
76 /// Application scoped state is useful for storing items
77 ///
78 /// # Examples
79 ///
80 /// ```no_run
81 /// # use async_std::task::block_on;
82 /// # fn main() -> Result<(), std::io::Error> { block_on(async {
83 /// #
84 /// use tide::Request;
85 ///
86 /// /// The shared application state.
87 /// #[derive(Clone)]
88 /// struct State {
89 /// name: String,
90 /// }
91 ///
92 /// // Define a new instance of the state.
93 /// let state = State {
94 /// name: "Nori".to_string()
95 /// };
96 ///
97 /// // Initialize the application with state.
98 /// let mut app = tide::with_state(state);
99 /// app.at("/").get(|req: Request<State>| async move {
100 /// Ok(format!("Hello, {}!", &req.state().name))
101 /// });
102 /// app.listen("127.0.0.1:8080").await?;
103 /// #
104 /// # Ok(()) }) }
105 /// ```
106 pub fn with_state(state: State) -> Self {
107 Self {
108 router: Arc::new(Router::new()),
109 middleware: Arc::new(vec![
110 #[cfg(feature = "cookies")]
111 Arc::new(cookies::CookiesMiddleware::new()),
112 #[cfg(feature = "logger")]
113 Arc::new(log::LogMiddleware::new()),
114 ]),
115 state,
116 }
117 }
118
119 /// Add a new route at the given `path`, relative to root.
120 ///
121 /// Routing means mapping an HTTP request to an endpoint. Here Tide applies
122 /// a "table of contents" approach, which makes it easy to see the overall
123 /// app structure. Endpoints are selected solely by the path and HTTP method
124 /// of a request: the path determines the resource and the HTTP verb the
125 /// respective endpoint of the selected resource. Example:
126 ///
127 /// ```rust,no_run
128 /// # let mut app = tide::Server::new();
129 /// app.at("/").get(|_| async { Ok("Hello, world!") });
130 /// ```
131 ///
132 /// A path is comprised of zero or many segments, i.e. non-empty strings
133 /// separated by '/'. There are two kinds of segments: concrete and
134 /// wildcard. A concrete segment is used to exactly match the respective
135 /// part of the path of the incoming request. A wildcard segment on the
136 /// other hand extracts and parses the respective part of the path of the
137 /// incoming request to pass it along to the endpoint as an argument. A
138 /// wildcard segment is written as `:name`, which creates an endpoint
139 /// parameter called `name`. It is not possible to define wildcard segments
140 /// with different names for otherwise identical paths.
141 ///
142 /// Alternatively a wildcard definitions can start with a `*`, for example
143 /// `*path`, which means that the wildcard will match to the end of given
144 /// path, no matter how many segments are left, even nothing.
145 ///
146 /// The name of the parameter can be omitted to define a path that matches
147 /// the required structure, but where the parameters are not required.
148 /// `:` will match a segment, and `*` will match an entire path.
149 ///
150 /// Here are some examples omitting the HTTP verb based endpoint selection:
151 ///
152 /// ```rust,no_run
153 /// # let mut app = tide::Server::new();
154 /// app.at("/");
155 /// app.at("/hello");
156 /// app.at("add_two/:num");
157 /// app.at("files/:user/*");
158 /// app.at("static/*path");
159 /// app.at("static/:context/:");
160 /// ```
161 ///
162 /// There is no fallback route matching, i.e. either a resource is a full
163 /// match or not, which means that the order of adding resources has no
164 /// effect.
165 pub fn at<'a>(&'a mut self, path: &str) -> Route<'a, State> {
166 let router = Arc::get_mut(&mut self.router)
167 .expect("Registering routes is not possible after the Server has started");
168 Route::new(router, path.to_owned())
169 }
170
171 /// Add middleware to an application.
172 ///
173 /// Middleware provides customization of the request/response cycle, such as compression,
174 /// logging, or header modification. Middleware is invoked when processing a request, and can
175 /// either continue processing (possibly modifying the response) or immediately return a
176 /// response. See the [`Middleware`] trait for details.
177 ///
178 /// Middleware can only be added at the "top level" of an application, and is processed in the
179 /// order in which it is applied.
180 pub fn with<M>(&mut self, middleware: M) -> &mut Self
181 where
182 M: Middleware<State>,
183 {
184 log::trace!("Adding middleware {}", middleware.name());
185 let m = Arc::get_mut(&mut self.middleware)
186 .expect("Registering middleware is not possible after the Server has started");
187 m.push(Arc::new(middleware));
188 self
189 }
190
191 /// Asynchronously serve the app with the supplied listener.
192 ///
193 /// This is a shorthand for calling `Server::bind`, logging the `ListenInfo`
194 /// instances from `Listener::info`, and then calling `Listener::accept`.
195 ///
196 /// # Examples
197 ///
198 /// ```no_run
199 /// # use async_std::task::block_on;
200 /// # fn main() -> Result<(), std::io::Error> { block_on(async {
201 /// #
202 /// let mut app = tide::new();
203 /// app.at("/").get(|_| async { Ok("Hello, world!") });
204 /// app.listen("127.0.0.1:8080").await?;
205 /// #
206 /// # Ok(()) }) }
207 /// ```
208 pub async fn listen<L: ToListener<State>>(self, listener: L) -> io::Result<()> {
209 let mut listener = listener.to_listener()?;
210 listener.bind(self).await?;
211 for info in listener.info().iter() {
212 log::info!("Server listening on {}", info);
213 }
214 listener.accept().await?;
215 Ok(())
216 }
217
218 /// Asynchronously bind the listener.
219 ///
220 /// Bind the listener. This starts the listening process by opening the
221 /// necessary network ports, but not yet accepting incoming connections.
222 /// `Listener::listen` should be called after this to start accepting
223 /// connections.
224 ///
225 /// When calling `Listener::info` multiple `ListenInfo` instances may be
226 /// returned. This is useful when using for example `ConcurrentListener`
227 /// which enables a single server to listen on muliple ports.
228 ///
229 /// # Examples
230 ///
231 /// ```no_run
232 /// # use async_std::task::block_on;
233 /// # fn main() -> Result<(), std::io::Error> { block_on(async {
234 /// #
235 /// use tide::prelude::*;
236 ///
237 /// let mut app = tide::new();
238 /// app.at("/").get(|_| async { Ok("Hello, world!") });
239 /// let mut listener = app.bind("127.0.0.1:8080").await?;
240 /// for info in listener.info().iter() {
241 /// println!("Server listening on {}", info);
242 /// }
243 /// listener.accept().await?;
244 /// #
245 /// # Ok(()) }) }
246 /// ```
247 pub async fn bind<L: ToListener<State>>(
248 self,
249 listener: L,
250 ) -> io::Result<<L as ToListener<State>>::Listener> {
251 let mut listener = listener.to_listener()?;
252 listener.bind(self).await?;
253 Ok(listener)
254 }
255
256 /// Respond to a `Request` with a `Response`.
257 ///
258 /// This method is useful for testing endpoints directly,
259 /// or for creating servers over custom transports.
260 ///
261 /// # Examples
262 ///
263 /// ```no_run
264 /// # #[async_std::main]
265 /// # async fn main() -> http_types::Result<()> {
266 /// #
267 /// use tide::http::{Url, Method, Request, Response};
268 ///
269 /// let mut app = tide::new();
270 /// app.at("/").get(|_| async { Ok("hello world") });
271 ///
272 /// let req = Request::new(Method::Get, Url::parse("https://example.com")?);
273 /// let res: Response = app.respond(req).await?;
274 ///
275 /// assert_eq!(res.status(), 200);
276 /// #
277 /// # Ok(()) }
278 /// ```
279 pub async fn respond<Req, Res>(&self, req: Req) -> http_types::Result<Res>
280 where
281 Req: Into<http_types::Request>,
282 Res: From<http_types::Response>,
283 {
284 let req = req.into();
285 let Self {
286 router,
287 state,
288 middleware,
289 } = self.clone();
290
291 let method = req.method().to_owned();
292 let Selection { endpoint, params } = router.route(&req.url().path(), method);
293 let route_params = vec![params];
294 let req = Request::new(state, req, route_params);
295
296 let next = Next {
297 endpoint,
298 next_middleware: &middleware,
299 };
300
301 let res = next.run(req).await;
302 let res: http_types::Response = res.into();
303 Ok(res.into())
304 }
305
306 /// Gets a reference to the server's state. This is useful for testing and nesting:
307 ///
308 /// # Example
309 ///
310 /// ```rust
311 /// # #[derive(Clone)] struct SomeAppState;
312 /// let mut app = tide::with_state(SomeAppState);
313 /// let mut admin = tide::with_state(app.state().clone());
314 /// admin.at("/").get(|_| async { Ok("nested app with cloned state") });
315 /// app.at("/").nest(admin);
316 /// ```
317 pub fn state(&self) -> &State {
318 &self.state
319 }
320}
321
322impl<State: Send + Sync + 'static> std::fmt::Debug for Server<State> {
323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 f.debug_struct("Server").finish()
325 }
326}
327
328impl<State: Clone> Clone for Server<State> {
329 fn clone(&self) -> Self {
330 Self {
331 router: self.router.clone(),
332 state: self.state.clone(),
333 middleware: self.middleware.clone(),
334 }
335 }
336}
337
338#[async_trait::async_trait]
339impl<State: Clone + Sync + Send + 'static, InnerState: Clone + Sync + Send + 'static>
340 Endpoint<State> for Server<InnerState>
341{
342 async fn call(&self, req: Request<State>) -> crate::Result {
343 let Request {
344 req,
345 mut route_params,
346 ..
347 } = req;
348 let path = req.url().path().to_owned();
349 let method = req.method().to_owned();
350 let router = self.router.clone();
351 let middleware = self.middleware.clone();
352 let state = self.state.clone();
353
354 let Selection { endpoint, params } = router.route(&path, method);
355 route_params.push(params);
356 let req = Request::new(state, req, route_params);
357
358 let next = Next {
359 endpoint,
360 next_middleware: &middleware,
361 };
362
363 Ok(next.run(req).await)
364 }
365}
366
367#[crate::utils::async_trait]
368impl<State: Clone + Send + Sync + Unpin + 'static> http_client::HttpClient for Server<State> {
369 async fn send(&self, req: crate::http::Request) -> crate::http::Result<crate::http::Response> {
370 self.respond(req).await
371 }
372}
373
374#[cfg(test)]
375mod test {
376 use crate as tide;
377
378 #[test]
379 fn allow_nested_server_with_same_state() {
380 let inner = tide::new();
381 let mut outer = tide::new();
382 outer.at("/foo").get(inner);
383 }
384
385 #[test]
386 fn allow_nested_server_with_different_state() {
387 let inner = tide::with_state(1);
388 let mut outer = tide::new();
389 outer.at("/foo").get(inner);
390 }
391}