sfo_http/http_server/
route.rs

1use std::fmt::Debug;
2use std::io;
3use std::path::Path;
4use std::sync::Arc;
5use http::Method;
6use crate::errors::HttpResult;
7use super::{Endpoint, Middleware, MiddlewareEndpoint, Request, Response, Router, ServeDir, ServeFile};
8
9#[allow(missing_debug_implementations)]
10pub struct Route<'a, Req: Request, Resp: Response> {
11    router: &'a mut Router<Req, Resp>,
12    path: String,
13    middleware: Vec<Arc<dyn Middleware<Req, Resp>>>,
14    prefix: bool,
15}
16
17impl<'a, Req: Request, Resp: Response> Route<'a, Req, Resp> {
18    pub(crate) fn new(router: &'a mut Router<Req, Resp>, path: String) -> Route<'a, Req, Resp> {
19        Route {
20            router,
21            path,
22            middleware: Vec::new(),
23            prefix: false,
24        }
25    }
26
27    /// Extend the route with the given `path`.
28    pub fn at<'b>(&'b mut self, path: &str) -> Route<'b, Req, Resp> {
29        let mut p = self.path.clone();
30
31        if !p.ends_with('/') && !path.starts_with('/') {
32            p.push('/');
33        }
34
35        if path != "/" {
36            p.push_str(path);
37        }
38
39        Route {
40            router: &mut self.router,
41            path: p,
42            middleware: self.middleware.clone(),
43            prefix: false,
44        }
45    }
46
47    #[must_use]
48    pub fn path(&self) -> &str {
49        &self.path
50    }
51
52    pub fn with<M>(&mut self, middleware: M) -> &mut Self
53    where
54        M: Middleware<Req, Resp>,
55    {
56        log::trace!(
57            "Adding middleware {} to route {:?}",
58            middleware.name(),
59            self.path
60        );
61        self.middleware.push(Arc::new(middleware));
62        self
63    }
64
65    pub fn reset_middleware(&mut self) -> &mut Self {
66        self.middleware.clear();
67        self
68    }
69
70    pub fn serve_dir(&mut self, dir: impl AsRef<Path>) -> io::Result<()> {
71        // Verify path exists, return error if it doesn't.
72        let dir = dir.as_ref().to_owned().canonicalize()?;
73        let prefix = self.path().to_string();
74        self.at("*").get(ServeDir::new(prefix, dir));
75        Ok(())
76    }
77
78    pub fn serve_file(&mut self, file: impl AsRef<Path>) -> io::Result<()> {
79        self.get(ServeFile::init(file)?);
80        Ok(())
81    }
82
83    pub fn method(&mut self, method: Method, ep: impl Endpoint<Req, Resp>) -> &mut Self {
84        if self.prefix {
85            let ep = StripPrefixEndpoint::new(ep);
86
87            self.router.add(
88                &self.path,
89                method.clone(),
90                MiddlewareEndpoint::wrap_with_middleware(ep.clone(), &self.middleware),
91            );
92            let wildcard = self.at("*--tide-path-rest");
93            wildcard.router.add(
94                &wildcard.path,
95                method,
96                MiddlewareEndpoint::wrap_with_middleware(ep, &wildcard.middleware),
97            );
98        } else {
99            self.router.add(
100                &self.path,
101                method,
102                MiddlewareEndpoint::wrap_with_middleware(ep, &self.middleware),
103            );
104        }
105        self
106    }
107
108    pub fn all(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
109        if self.prefix {
110            let ep = StripPrefixEndpoint::new(ep);
111
112            self.router.add_all(
113                &self.path,
114                MiddlewareEndpoint::wrap_with_middleware(ep.clone(), &self.middleware),
115            );
116            let wildcard = self.at("*--tide-path-rest");
117            wildcard.router.add_all(
118                &wildcard.path,
119                MiddlewareEndpoint::wrap_with_middleware(ep, &wildcard.middleware),
120            );
121        } else {
122            self.router.add_all(
123                &self.path,
124                MiddlewareEndpoint::wrap_with_middleware(ep, &self.middleware),
125            );
126        }
127        self
128    }
129
130    /// Add an endpoint for `GET` requests
131    pub fn get(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
132        self.method(Method::GET, ep);
133        self
134    }
135
136    /// Add an endpoint for `HEAD` requests
137    pub fn head(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
138        self.method(Method::HEAD, ep);
139        self
140    }
141
142    /// Add an endpoint for `PUT` requests
143    pub fn put(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
144        self.method(Method::PUT, ep);
145        self
146    }
147
148    /// Add an endpoint for `POST` requests
149    pub fn post(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
150        self.method(Method::POST, ep);
151        self
152    }
153
154    /// Add an endpoint for `DELETE` requests
155    pub fn delete(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
156        self.method(Method::DELETE, ep);
157        self
158    }
159
160    /// Add an endpoint for `OPTIONS` requests
161    pub fn options(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
162        self.method(Method::OPTIONS, ep);
163        self
164    }
165
166    /// Add an endpoint for `CONNECT` requests
167    pub fn connect(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
168        self.method(Method::CONNECT, ep);
169        self
170    }
171
172    /// Add an endpoint for `PATCH` requests
173    pub fn patch(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
174        self.method(Method::PATCH, ep);
175        self
176    }
177
178    /// Add an endpoint for `TRACE` requests
179    pub fn trace(&mut self, ep: impl Endpoint<Req, Resp>) -> &mut Self {
180        self.method(Method::TRACE, ep);
181        self
182    }
183}
184
185#[derive(Debug)]
186struct StripPrefixEndpoint<E>(std::sync::Arc<E>);
187
188impl<E> StripPrefixEndpoint<E> {
189    fn new(ep: E) -> Self {
190        Self(std::sync::Arc::new(ep))
191    }
192}
193
194impl<E> Clone for StripPrefixEndpoint<E> {
195    fn clone(&self) -> Self {
196        Self(self.0.clone())
197    }
198}
199
200#[async_trait::async_trait]
201impl<Req: Request, Resp: Response, E> Endpoint<Req, Resp> for StripPrefixEndpoint<E>
202where
203    E: Endpoint<Req, Resp>,
204{
205    async fn call(&self, req: Req) -> HttpResult<Resp> {
206        self.0
207            .call(req)
208            .await
209    }
210}