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}