brio/
lib.rs

1#![feature(async_closure)]
2#![feature(or_patterns)]
3#![feature(exclusive_range_pattern)]
4
5mod body;
6mod request;
7mod response;
8mod router;
9mod server;
10
11use async_std::{fs, io::ReadExt, task};
12use router::{Middleware, Path, Route, Router};
13use std::{future::Future, pin::Pin, str, sync::Arc};
14
15pub use body::ChunkedBody;
16pub use request::{Encoding, Method, Request};
17pub use response::{Response, Status};
18pub use router::Ctx;
19
20pub(crate) const BUF_LEN: usize = 2;
21pub(crate) const KEEP_ALIVE_TIMEOUT: u64 = 10;
22
23pub(crate) type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
24pub(crate) type Sender<T> = futures::channel::mpsc::Sender<T>;
25pub(crate) type Receiver<T> = futures::channel::mpsc::Receiver<T>;
26pub(crate) type BoxFuture<'a, Response> = Pin<Box<dyn Future<Output = Response> + Send + 'static>>;
27
28pub struct App<Routes> {
29    router: Router<Routes>,
30}
31
32impl App<()> {
33    pub fn new() -> App<()> {
34        Self::with_routes(())
35    }
36    pub fn run(self, port: u32) -> Result<()> {
37        let router = Arc::new(self.router);
38        task::block_on(server::accept_loop("127.0.0.1", port, router))
39    }
40    pub fn get(&mut self, path: &'static str, handler: impl Route) -> &Self {
41        self.add_route(Path::new(Method::Get, path.to_owned()), handler);
42        self
43    }
44    pub fn post(&mut self, path: &'static str, handler: impl Route) -> &Self {
45        self.add_route(Path::new(Method::Post, path.to_owned()), handler);
46        self
47    }
48    pub fn put(&mut self, path: &'static str, handler: impl Route) -> &Self {
49        self.add_route(Path::new(Method::Put, path.to_owned()), handler);
50        self
51    }
52    pub fn delete(&mut self, path: &'static str, handler: impl Route) -> &Self {
53        self.add_route(Path::new(Method::Delete, path.to_owned()), handler);
54        self
55    }
56    pub fn middleware(&mut self, path: &'static str, middleware: impl Middleware) -> &Self {
57        self.add_middleware(Path::all(path.to_owned()), middleware);
58        self
59    }
60    pub fn get_middleware(&mut self, path: &'static str, middleware: impl Middleware) -> &Self {
61        self.add_middleware(Path::new(Method::Get, path.to_owned()), middleware);
62        self
63    }
64    pub fn post_middleware(&mut self, path: &'static str, middleware: impl Middleware) -> &Self {
65        self.add_middleware(Path::new(Method::Post, path.to_owned()), middleware);
66        self
67    }
68    pub fn put_middleware(&mut self, path: &'static str, middleware: impl Middleware) -> &Self {
69        self.add_middleware(Path::new(Method::Put, path.to_owned()), middleware);
70        self
71    }
72    pub fn delete_middleware(&mut self, path: &'static str, middleware: impl Middleware) -> &Self {
73        self.add_middleware(Path::new(Method::Delete, path.to_owned()), middleware);
74        self
75    }
76    pub fn files(&mut self, path: &'static str, src: &'static str) -> &Self {
77        self.add_middleware(
78            Path::new(Method::Get, path.to_owned()),
79            Files::new(path, src),
80        );
81        self
82    }
83    fn add_route(&mut self, route: Path, handler: impl Route) {
84        self.router.routes.insert(route, Box::new(handler));
85    }
86    fn add_middleware(&mut self, route: Path, middleware: impl Middleware) {
87        self.router.middleware.push((route, Arc::new(middleware)));
88    }
89}
90
91impl<Routes: Send + Sync + Copy + 'static> App<Routes> {
92    pub fn with_routes(routes: Routes) -> App<Routes> {
93        App {
94            router: Router::new(routes),
95        }
96    }
97}
98
99async fn not_found(_req: Request) -> Response {
100    Response::with_status(Status::NotFound)
101}
102
103pub struct Files {
104    pub path: &'static str,
105    pub src: &'static str,
106}
107
108impl Files {
109    pub fn new(path: &'static str, src: &'static str) -> Files {
110        Files { path, src }
111    }
112}
113
114impl Middleware for Files {
115    fn handle(&self, ctx: Ctx) -> BoxFuture<Response> {
116        let filepath = ctx.req.path.replace(&self.path, "");
117        let src = self.src.clone();
118        Box::pin(async move {
119            let path = format!("{}{}", src, filepath);
120            match fs::canonicalize(path).await {
121                Ok(path) => match async_std::fs::File::open(path).await {
122                    Ok(mut file) => {
123                        let mut res = Response::with_status(Status::Ok);
124                        let mut buf = vec![];
125                        let res = match file.read_to_end(&mut buf).await {
126                            Ok(_) => {
127                                let mime = mime_guess::from_path(filepath)
128                                    .first()
129                                    .unwrap_or(mime::APPLICATION_OCTET_STREAM);
130
131                                res.set_bytes(buf, mime);
132                                res
133                            }
134                            Err(_) => Response::with_status(Status::NotFound),
135                        };
136                        res
137                    }
138                    Err(_) => Response::with_status(Status::NotFound),
139                },
140                Err(_) => Response::with_status(Status::NotFound),
141            }
142        })
143    }
144}