canteen/
lib.rs

1// Copyright (c) 2016
2// Jeff Nettleton
3//
4// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
5// file may not be copied, modified, or distributed except according to those
6// terms
7
8//! # Canteen
9//!
10//! ## Description
11//!
12//! A pure Rust clone of [Flask](http://flask.pocoo.org), a simple but powerful Python
13//! web framework.
14//!
15//! The principle behind Canteen is simple -- handler functions are defined as simple
16//! Rust functions that take a `Request` and return a `Response`. Handlers are then attached
17//! to one or more routes and HTTP methods/verbs. Routes are specified using a simple
18//! syntax that lets you define variables within them; variables that can then be
19//! extracted to perform various operations. Currently, the following variable types can
20//! be used:
21//!
22//! - `<str:name>` will match anything inside a path segment, returns a `String`
23//! - `<int:name>` will return a signed integer (`i32`) from a path segment
24//!   - ex: `cnt.add_route("/api/foo/<int:foo_id>", &[Method::Get], my_handler)` will match
25//!   `"/api/foo/123"` but not `"/api/foo/123.34"` or `"/api/foo/bar"`
26//! - `<uint:name>` will return an unsigned integer (`u32`)
27//! - `<float:name>` does the same thing as the `int` parameter definition, but matches numbers
28//! with decimal points and returns an `f32`
29//! - `<path:name>` will greedily take all path data contained, returns a `String`
30//!   - ex: `cnt.add_route("/static/<path:name>", &[Method::Get], utils::static_file)` will
31//!   serve anything in the `/static/` directory as a file
32//!
33//! After the handlers are attached to routes, the next step is to simply start the
34//! server. Any time a request is received, it is dispatched with the associated handler
35//! to a threadpool worker. The worker notifies the parent process when it's finished,
36//! and then the response is transmitted back to the client. Pretty straightforward stuff!
37//!
38//! ## Example
39//!
40//! ```rust
41//! extern crate canteen;
42//!
43//! use canteen::{Canteen, Request, Response, Method};
44//! use canteen::utils;
45//!
46//! fn hello_handler(_: &Request) -> Response {
47//!     let mut res = Response::new();
48//!
49//!     res.set_status(200);
50//!     res.set_content_type("text/plain");
51//!     res.append("Hello, world!");
52//!
53//!     res
54//! }
55//!
56//! fn double_handler(req: &Request) -> Response {
57//!     let to_dbl: i32 = req.get("to_dbl");
58//!
59//!     /* simpler response generation syntax */
60//!     utils::make_response(format!("{}", to_dbl * 2), "text/plain", 200)
61//! }
62//!
63//! fn main() {
64//!     let mut cnt = Canteen::new();
65//!
66//!     // bind to an address
67//!     cnt.bind(("127.0.0.1", 8888));
68//!
69//!     // set the default route handler to show a 404 message
70//!     cnt.set_default(utils::err_404);
71//!
72//!     // respond to requests to / with "Hello, world!"
73//!     cnt.add_route("/", &[Method::Get], hello_handler);
74//!
75//!     // pull a variable from the path and do something with it
76//!     cnt.add_route("/double/<int:to_dbl>", &[Method::Get], double_handler);
77//!
78//!     // serve raw files from the /static/ directory
79//!     cnt.add_route("/static/<path:path>", &[Method::Get], utils::static_file);
80//! }
81//! ```
82
83pub mod utils;
84pub mod route;
85pub mod request;
86pub mod response;
87
88#[cfg(test)]
89#[macro_use]
90extern crate serde_derive;
91
92use std::str::FromStr;
93use std::io::Result;
94use std::io::prelude::*;
95use std::net::ToSocketAddrs;
96use std::collections::HashMap;
97use std::collections::HashSet;
98
99use threadpool::ThreadPool;
100use mio::tcp::{TcpListener, TcpStream};
101use mio::util::Slab;
102use mio::*;
103
104pub use crate::request::*;
105pub use crate::response::*;
106
107struct Client {
108    sock:   TcpStream,
109    token:  Token,
110    events: EventSet,
111    i_buf:  Vec<u8>,
112    o_buf:  Vec<u8>,
113}
114
115impl Client {
116    fn new(sock: TcpStream, token: Token) -> Client {
117        Client {
118            sock,
119            token,
120            events: EventSet::hup(),
121            i_buf:  Vec::with_capacity(2048),
122            o_buf:  Vec::new(),
123        }
124    }
125
126    fn receive(&mut self) -> Result<bool> {
127        let mut bytes_read: usize = 0;
128
129        loop {
130            let mut buf: Vec<u8> = Vec::with_capacity(2048);
131            match self.sock.try_read_buf(&mut buf) {
132                Ok(size)  => {
133                    match size {
134                        Some(bytes) => {
135                            self.i_buf.extend(buf);
136                            bytes_read += bytes;
137                        },
138                        None    => {
139                            self.events.remove(EventSet::readable());
140                            self.events.insert(EventSet::writable());
141                            break;
142                        },
143                    }
144                },
145                Err(_)  => {
146                    self.events.remove(EventSet::readable());
147                    self.events.insert(EventSet::writable());
148                    break;
149                },
150            };
151        }
152
153        Ok(bytes_read > 0)
154    }
155
156    // write the client's output buffer to the socket.
157    //
158    // the following return values mean:
159    //  - Ok(true):  we can close the connection
160    //  - Ok(false): keep listening for writeable event and continue next time
161    //  - Err(e):    something dun fucked up
162    fn send(&mut self) -> Result<bool> {
163        if self.o_buf.is_empty() {
164            return Ok(false);
165        }
166
167        while !self.o_buf.is_empty() {
168            match self.sock.write(&self.o_buf.as_slice()) {
169                Ok(sz)  => {
170                    if sz == self.o_buf.len() {
171                        // we did it!
172                        self.events.remove(EventSet::writable());
173                        break;
174                    } else {
175                        // keep going
176                        self.o_buf = self.o_buf.split_off(sz);
177                    }
178                },
179                Err(_)  => {
180                    return Ok(true);
181                }
182            }
183        }
184
185        Ok(true)
186    }
187
188    fn register(&mut self, evl: &mut EventLoop<Canteen>) -> Result<()> {
189        self.events.insert(EventSet::readable());
190        evl.register(&self.sock, self.token, self.events, PollOpt::edge() | PollOpt::oneshot())
191    }
192
193    fn reregister(&mut self, evl: &mut EventLoop<Canteen>) -> Result<()> {
194        evl.reregister(&self.sock, self.token, self.events, PollOpt::edge() | PollOpt::oneshot())
195    }
196}
197
198/// The primary struct provided by the library. The aim is to have a similar
199/// interface to Flask, the Python microframework.
200pub struct Canteen {
201    routes:  HashMap<route::RouteDef, route::Route>,
202    rcache:  HashMap<route::RouteDef, route::RouteDef>,
203    server:  Option<TcpListener>,
204    token:   Token,
205    conns:   Slab<Client>,
206    default: fn(&Request) -> Response,
207    tpool:   ThreadPool,
208}
209
210impl Handler for Canteen {
211    type Timeout = ();
212    type Message = (Token, Vec<u8>);
213
214    fn ready(&mut self, evl: &mut EventLoop<Canteen>, token: Token, events: EventSet) {
215        if events.is_error() || events.is_hup() {
216            self.reset_connection(token);
217            return;
218        }
219
220        if events.is_readable() {
221            if self.token == token {
222                let sock = self.accept().unwrap();
223
224                if let Some(token) = self.conns.insert_with(|token| Client::new(sock, token)) {
225                    self.get_client(token).register(evl).ok();
226                }
227
228                self.reregister(evl);
229            } else {
230                self.readable(evl, token)
231                    .and_then(|_| self.get_client(token)
232                                      .reregister(evl)).ok();
233            }
234
235            return;
236        }
237
238        if events.is_writable() {
239            match self.get_client(token).send() {
240                Ok(true)    => { self.reset_connection(token); },
241                Ok(false)   => { let _ = self.get_client(token).reregister(evl); },
242                Err(_)      => {},
243            }
244        }
245    }
246
247    fn notify(&mut self, evl: &mut EventLoop<Canteen>, msg: (Token, Vec<u8>)) {
248        let (token, output) = msg;
249        let client = self.get_client(token);
250
251        client.o_buf = output;
252        let _ = client.reregister(evl);
253    }
254}
255
256impl Canteen {
257    /// Creates a new Canteen instance.
258    ///
259    /// # Examples
260    ///
261    /// ```rust
262    /// use canteen::Canteen;
263    ///
264    /// let cnt = Canteen::new();
265    /// ```
266    pub fn new() -> Canteen {
267        Canteen {
268            routes:  HashMap::new(),
269            rcache:  HashMap::new(),
270            server:  None,
271            token:   Token(1),
272            conns:   Slab::new_starting_at(Token(2), 2048),
273            default: utils::err_404,
274            tpool:   ThreadPool::new(255),
275        }
276    }
277
278    /// Bind to an address on which to listen for connections
279    /// # Examples
280    /// ```rust,ignore
281    /// use canteen::Canteen;
282    ///
283    /// let mut cnt = Canteen::new();
284    /// cnt.bind(("127.0.0.1", 8080));
285    /// ```
286    pub fn bind<A: ToSocketAddrs>(&mut self, addr: A) {
287        self.server = Some(TcpListener::bind(&addr.to_socket_addrs().unwrap().next().unwrap()).unwrap());
288    }
289
290
291    /// Adds a new route definition to be handled by Canteen.
292    ///
293    /// # Examples
294    ///
295    /// ```rust
296    /// use canteen::{Canteen, Request, Response, Method};
297    /// use canteen::utils;
298    ///
299    /// fn handler(_: &Request) -> Response {
300    ///     utils::make_response("<b>Hello, world!</b>", "text/html", 200)
301    /// }
302    ///
303    /// fn main() {
304    ///     let mut cnt = Canteen::new();
305    ///     cnt.add_route("/hello", &[Method::Get], handler);
306    /// }
307    /// ```
308    pub fn add_route(&mut self, path: &str, mlist: &[Method],
309                     handler: fn(&Request) -> Response) -> &mut Canteen {
310        let mut methods: HashSet<Method> = HashSet::new();
311
312        // make them unique
313        for m in mlist {
314            methods.insert(*m);
315        }
316
317        for m in methods {
318            let rd = route::RouteDef {
319                pathdef: String::from(path),
320                method:  m,
321            };
322
323            if self.routes.contains_key(&rd) {
324                panic!("a route handler for {} has already been defined!", path);
325            }
326
327            self.routes.insert(rd, route::Route::new(&path, m, handler));
328        }
329
330        self
331    }
332
333    /// Defines a default route for undefined paths.
334    ///
335    /// # Examples
336    ///
337    /// ```rust
338    /// use canteen::Canteen;
339    /// use canteen::utils;
340    ///
341    /// let mut cnt = Canteen::new();
342    /// cnt.set_default(utils::err_404);
343    /// ```
344    pub fn set_default(&mut self, handler: fn(&Request) -> Response) -> &mut Canteen {
345        self.default = handler;
346
347        self
348    }
349
350    fn get_client(&mut self, token: Token) -> &mut Client {
351        self.conns.get_mut(token).unwrap()
352    }
353
354    fn accept(&mut self) -> Result<TcpStream> {
355        if let Some(ref server) = self.server {
356            if let Ok(s) = server.accept() {
357                if let Some((sock, _)) = s {
358                    return Ok(sock);
359                }
360            }
361        }
362
363        Err(std::io::Error::new(
364            std::io::ErrorKind::ConnectionAborted,
365            "connection aborted prematurely".to_string()
366        ))
367    }
368
369    fn handle_request(&mut self, token: Token, tx: Sender<(Token, Vec<u8>)>, rqstr: &str) {
370        let mut req = Request::from_str(&rqstr).unwrap();
371        let mut handler: fn(&Request) -> Response = self.default;
372        let resolved = route::RouteDef {
373            pathdef: req.path.clone(),
374            method:  req.method,
375        };
376
377        if self.rcache.contains_key(&resolved) {
378            let route = &self.routes[&self.rcache[&resolved]];
379
380            handler = route.handler;
381            req.params = route.parse(&req.path);
382        } else {
383            for (path, route) in &self.routes {
384                if route.is_match(&req) {
385                    handler = route.handler;
386                    req.params = route.parse(&req.path);
387                    self.rcache.insert(resolved, (*path).clone());
388                    break;
389                }
390            }
391        }
392
393        self.tpool.execute(move || {
394            let _ = tx.send((token, handler(&req).gen_output()));
395        });
396    }
397
398    fn readable(&mut self, evl: &mut EventLoop<Canteen>, token: Token) -> Result<bool> {
399        if let Ok(true) = self.get_client(token).receive() {
400            let buf = self.get_client(token).i_buf.clone();
401            if let Ok(rqstr) = String::from_utf8(buf) {
402                self.handle_request(token, evl.channel(), &rqstr);
403            } else {
404                return Ok(false);
405            }
406        }
407
408        Ok(true)
409    }
410
411    fn reset_connection(&mut self, token: Token) {
412        // kill the connection
413        self.conns.remove(token);
414    }
415
416    fn register(&mut self, evl: &mut EventLoop<Canteen>) -> Result<()> {
417        if let Some(ref server) = self.server {
418            return evl.register(server, self.token, EventSet::readable(), PollOpt::edge() | PollOpt::oneshot());
419        }
420
421        Ok(())
422    }
423
424    fn reregister(&mut self, evl: &mut EventLoop<Canteen>) {
425        if let Some(ref server) = self.server {
426            evl.reregister(server, self.token,
427                                 EventSet::readable(),
428                                 PollOpt::edge() | PollOpt::oneshot()).ok();
429        }
430    }
431
432    /// Creates the listener and starts a Canteen server's event loop.
433    ///
434    /// # Examples
435    ///
436    /// ```rust
437    /// use canteen::Canteen;
438    ///
439    /// let mut cnt = Canteen::new();
440    /// cnt.run();
441    /// ```
442    pub fn run(&mut self) {
443        let mut evl = match EventLoop::new() {
444            Ok(event_loop)  => event_loop,
445            Err(_)          => panic!("unable to initiate event loop"),
446        };
447
448        match self.server {
449            None    => println!("server not bound to an address!"),
450            Some(_) => {
451                self.register(&mut evl).ok();
452                evl.run(self).unwrap();
453            },
454        };
455    }
456}
457
458impl Default for Canteen {
459    fn default() -> Self {
460        Canteen::new()
461    }
462}