1extern crate num_cpus;
3
4use std::convert::Into;
5use std::sync::RwLock;
6use std::fmt;
7use std::collections::HashMap;
8use std::error::Error;
9use std::fs::File;
10use std::path::PathBuf;
11use std::net::ToSocketAddrs;
12
13use serde_json::{Value};
14use serde::Serialize;
15use handlebars::Handlebars;
16use hyper;
17use hyper::method::Method;
18use hyper::status::StatusCode;
19use hyper::server::Request as HTTPRequest;
20use hyper::server::Response as HTTPResponse;
21
22use types::{
23 PencilError,
24 PenHTTPError,
25 PenUserError,
26
27 UserError,
28 PencilResult,
29 ViewFunc,
30 HTTPErrorHandler,
31 UserErrorHandler,
32 BeforeRequestFunc,
33 AfterRequestFunc,
34 TeardownRequestFunc,
35};
36use wrappers::{
37 Request,
38 Response,
39};
40use helpers::{PathBound, send_from_directory_range, redirect};
41use config::Config;
42use logging;
43use serving::run_server;
44use routing::{Map, Rule, Matcher};
45use testing::PencilClient;
46use http_errors::{HTTPError, NotFound, InternalServerError};
47use templating::{render_template, render_template_string, load_template};
48use module::Module;
49use typemap::{ShareMap, Key};
50use hyper::header::{IfModifiedSince, LastModified, HttpDate, CacheControl, CacheDirective};
51use time;
52
53pub struct Pencil {
56 pub root_path: String,
58 pub name: String,
60 pub static_folder: String,
63 pub static_url_path: String,
65 pub template_folder: String,
68 pub config: Config,
70 pub extensions: ShareMap,
72 pub handlebars_registry: RwLock<Box<Handlebars>>,
74 pub url_map: Map,
76 pub modules: HashMap<String, Module>,
78 view_functions: HashMap<String, ViewFunc>,
80 before_request_funcs: Vec<Box<BeforeRequestFunc>>,
81 after_request_funcs: Vec<Box<AfterRequestFunc>>,
82 teardown_request_funcs: Vec<Box<TeardownRequestFunc>>,
83 http_error_handlers: HashMap<u16, Box<HTTPErrorHandler>>,
84 user_error_handlers: HashMap<String, Box<UserErrorHandler>>,
85}
86
87fn default_config() -> Config {
88 let mut config = Config::new();
89 config.set("DEBUG", Value::Bool(false));
90 config.set("TESTING", Value::Bool(false));
91 config
92}
93
94impl Pencil {
95 pub fn new(root_path: &str) -> Pencil {
109 Pencil {
110 root_path: root_path.to_string(),
111 name: root_path.to_string(),
112 static_folder: String::from("static"),
113 static_url_path: String::from("/static"),
114 template_folder: String::from("templates"),
115 config: default_config(),
116 extensions: ShareMap::custom(),
117 handlebars_registry: RwLock::new(Box::new(Handlebars::new())),
118 url_map: Map::new(),
119 modules: HashMap::new(),
120 view_functions: HashMap::new(),
121 before_request_funcs: vec![],
122 after_request_funcs: vec![],
123 teardown_request_funcs: vec![],
124 http_error_handlers: HashMap::new(),
125 user_error_handlers: HashMap::new(),
126 }
127 }
128
129 pub fn is_debug(&self) -> bool {
132 self.config.get_boolean("DEBUG", false)
133 }
134
135 pub fn is_testing(&self) -> bool {
138 self.config.get_boolean("TESTING", false)
139 }
140
141 pub fn set_debug(&mut self, flag: bool) {
145 self.config.set("DEBUG", Value::Bool(flag));
146 }
147
148 pub fn set_testing(&mut self, flag: bool) {
152 self.config.set("TESTING", Value::Bool(flag));
153 }
154
155 pub fn set_log_level(&self) {
159 logging::set_log_level(self);
160 }
161
162 pub fn route<M: Into<Matcher>, N: AsRef<[Method]>>(&mut self, rule: M, methods: N, endpoint: &str, view_func: ViewFunc) {
173 self.add_url_rule(rule.into(), methods.as_ref(), endpoint, view_func);
174 }
175
176 pub fn get<M: Into<Matcher>>(&mut self, rule: M, endpoint: &str, view_func: ViewFunc) {
179 self.route(rule, &[Method::Get], endpoint, view_func);
180 }
181
182 pub fn post<M: Into<Matcher>>(&mut self, rule: M, endpoint: &str, view_func: ViewFunc) {
185 self.route(rule, &[Method::Post], endpoint, view_func);
186 }
187
188 pub fn delete<M: Into<Matcher>>(&mut self, rule: M, endpoint: &str, view_func: ViewFunc) {
191 self.route(rule, &[Method::Delete], endpoint, view_func);
192 }
193
194 pub fn patch<M: Into<Matcher>>(&mut self, rule: M, endpoint: &str, view_func: ViewFunc) {
197 self.route(rule, &[Method::Patch], endpoint, view_func);
198 }
199
200 pub fn put<M: Into<Matcher>>(&mut self, rule: M, endpoint: &str, view_func: ViewFunc) {
203 self.route(rule, &[Method::Put], endpoint, view_func);
204 }
205
206 pub fn add_url_rule(&mut self, matcher: Matcher, methods: &[Method], endpoint: &str, view_func: ViewFunc) {
208 let url_rule = Rule::new(matcher, methods, endpoint);
209 self.url_map.add(url_rule);
210 self.view_functions.insert(endpoint.to_string(), view_func);
211 }
212
213 pub fn register_module(&mut self, module: Module) {
215 module.register(self);
216 }
217
218 pub fn enable_static_file_handling(&mut self) {
220 let mut rule = self.static_url_path.clone();
221 rule = rule + "/<filename:path>";
222 let rule_str: &str = &rule;
223 self.route(rule_str, &[Method::Get], "static", send_app_static_file);
224 }
225
226 pub fn enable_static_cached_file_handling(&mut self, max_age: ::std::time::Duration) {
229 let mut rule = self.static_url_path.clone();
230 rule = rule + "/<filename:path>";
231 let rule_str: &str = &rule;
232 let mut tm = time::now_utc();
233 tm.tm_nsec = 0;
234 self.extensions.insert::<TimeAtServerStartKey>(tm);
235 self.extensions.insert::<MaxAgeKey>(max_age);
236 self.route(rule_str, &[Method::Get], "static", send_app_static_file_with_cache);
237 }
238
239 pub fn before_request<F: Fn(&mut Request) -> Option<PencilResult> + Send + Sync + 'static>(&mut self, f: F) {
241 self.before_request_funcs.push(Box::new(f));
242 }
243
244 pub fn after_request<F: Fn(&Request, &mut Response) + Send + Sync + 'static>(&mut self, f: F) {
247 self.after_request_funcs.push(Box::new(f));
248 }
249
250 pub fn teardown_request<F: Fn(Option<&PencilError>) + Send + Sync + 'static>(&mut self, f: F) {
253 self.teardown_request_funcs.push(Box::new(f));
254 }
255
256 pub fn register_http_error_handler<F: Fn(HTTPError) -> PencilResult + Send + Sync + 'static>(&mut self, status_code: u16, f: F) {
259 self.http_error_handlers.insert(status_code, Box::new(f));
260 }
261
262 pub fn register_user_error_handler<F: Fn(UserError) -> PencilResult + Send + Sync + 'static>(&mut self, error_desc: &str, f: F) {
265 self.user_error_handlers.insert(error_desc.to_string(), Box::new(f));
266 }
267
268 pub fn httperrorhandler<F: Fn(HTTPError) -> PencilResult + Send + Sync + 'static>(&mut self, status_code: u16, f: F) {
288 self.register_http_error_handler(status_code, f);
289 }
290
291 pub fn usererrorhandler<F: Fn(UserError) -> PencilResult + Send + Sync + 'static>(&mut self, error_desc: &str, f: F) {
369 self.register_user_error_handler(error_desc, f);
370 }
371
372 #[allow(dead_code)]
381 fn test_client(&self) -> PencilClient {
382 PencilClient::new(self)
383 }
384
385 fn preprocess_request(&self, request: &mut Request) -> Option<PencilResult> {
388 if let Some(module) = self.get_module(request.module_name()) {
389 for func in &module.before_request_funcs {
390 if let Some(result) = func(request) {
391 return Some(result);
392 }
393 }
394 }
395 for func in &self.before_request_funcs {
396 if let Some(result) = func(request) {
397 return Some(result);
398 }
399 }
400 None
401 }
402
403 fn dispatch_request(&self, request: &mut Request) -> PencilResult {
406 if let Some(ref routing_error) = request.routing_error {
407 return Err(PenHTTPError(routing_error.clone()));
408 }
409 if let Some((ref redirect_url, redirect_code)) = request.routing_redirect {
410 return redirect(redirect_url, redirect_code);
411 }
412 if let Some(default_options_response) = self.make_default_options_response(request) {
413 return Ok(default_options_response);
414 }
415 match self.view_functions.get(&request.endpoint().unwrap()) {
416 Some(&view_func) => {
417 view_func(request)
418 },
419 None => {
420 Err(PenHTTPError(NotFound))
421 }
422 }
423 }
424
425 fn make_default_options_response(&self, request: &Request) -> Option<Response> {
427 if let Some(ref rule) = request.url_rule {
428 if rule.provide_automatic_options && request.method() == Method::Options {
431 let url_adapter = request.url_adapter();
432 let mut response = Response::new_empty();
433 response.headers.set(hyper::header::Allow(url_adapter.allowed_methods()));
434 return Some(response);
435 }
436 }
437 None
438 }
439
440 fn get_module(&self, module_name: Option<String>) -> Option<&Module> {
442 if let Some(name) = module_name {
443 self.modules.get(&name)
444 } else {
445 None
446 }
447 }
448
449 fn process_response(&self, request: &Request, response: &mut Response) {
451 if let Some(module) = self.get_module(request.module_name()) {
452 for func in module.after_request_funcs.iter().rev() {
453 func(request, response);
454 }
455 }
456 for func in self.after_request_funcs.iter().rev() {
457 func(request, response);
458 }
459 }
460
461 fn do_teardown_request(&self, request: &Request, e: Option<&PencilError>) {
463 if let Some(module) = self.get_module(request.module_name()) {
464 for func in module.teardown_request_funcs.iter().rev() {
465 func(e);
466 }
467 }
468 for func in self.teardown_request_funcs.iter().rev() {
469 func(e);
470 }
471 }
472
473 fn handle_all_error(&self, request: &Request, e: PencilError) -> PencilResult {
475 match e {
476 PenHTTPError(e) => self.handle_http_error(request, e),
477 PenUserError(e) => self.handle_user_error(request, e),
478 }
479 }
480
481 fn handle_user_error(&self, request: &Request, e: UserError) -> PencilResult {
483 if let Some(module) = self.get_module(request.module_name()) {
484 if let Some(handler) = module.user_error_handlers.get(&e.desc) {
485 return handler(e);
486 }
487 }
488 if let Some(handler) = self.user_error_handlers.get(&e.desc) {
489 return handler(e);
490 }
491 Err(PenUserError(e))
492 }
493
494 fn handle_http_error(&self, request: &Request, e: HTTPError) -> PencilResult {
496 if let Some(module) = self.get_module(request.module_name()) {
497 if let Some(handler) = module.http_error_handlers.get(&e.code()) {
498 return handler(e);
499 }
500 }
501 if let Some(handler) = self.http_error_handlers.get(&e.code()) {
502 return handler(e);
503 }
504 Ok(e.to_response())
505 }
506
507 fn handle_error(&self, request: &Request, e: &PencilError) -> Response {
510 self.log_error(request, e);
511 let internal_server_error = InternalServerError;
512 if let Ok(response) = self.handle_http_error(request, internal_server_error) {
513 return response;
514 } else {
515 let e = InternalServerError;
516 return e.to_response();
517 }
518 }
519
520 fn log_error(&self, request: &Request, e: &PencilError) {
522 error!("Error on {} [{}]: {}", request.path(), request.method(), e.description());
523 }
524
525 fn full_dispatch_request(&self, request: &mut Request) -> Result<Response, PencilError> {
528 let result = match self.preprocess_request(request) {
529 Some(result) => result,
530 None => self.dispatch_request(request),
531 };
532 let rv = match result {
533 Ok(response) => Ok(response),
534 Err(e) => self.handle_all_error(request, e),
535 };
536 match rv {
537 Ok(mut response) => {
538 self.process_response(request, &mut response);
539 Ok(response)
540 },
541 Err(e) => Err(e),
542 }
543 }
544
545 pub fn register_template(&mut self, template_name: &str) {
547 let registry_write_rv = self.handlebars_registry.write();
548 if registry_write_rv.is_err() {
549 panic!("Can't write handlebars registry");
550 }
551 let mut registry = registry_write_rv.unwrap();
552 match load_template(self, template_name) {
553 Some(source_rv) => {
554 match source_rv {
555 Ok(source) => {
556 if let Err(err) = registry.register_template_string(template_name, source) {
557 panic!(format!("Template compile error: {}", err));
558 }
559 },
560 Err(err) => {
561 panic!(format!("Template {} can't be loaded: {}", template_name, err));
562 }
563 }
564 },
565 None => {
566 panic!(format!("Template not found: {}", template_name));
567 }
568 }
569 }
570
571 pub fn render_template<T: Serialize>(&self, template_name: &str, context: &T)
576 -> PencilResult
577 {
578 render_template(self, template_name, context)
579 }
580
581 pub fn render_template_string<T: Serialize>(&self, source: &str, context: &T)
587 -> PencilResult
588 {
589 render_template_string(self, source, context)
590 }
591
592 pub fn handle_request(&self, request: &mut Request) -> Response {
594 request.match_request();
595 match self.full_dispatch_request(request) {
596 Ok(response) => {
597 self.do_teardown_request(request, None);
598 return response;
599 },
600 Err(e) => {
601 let response = self.handle_error(request, &e);
602 self.do_teardown_request(request, Some(&e));
603 return response;
604 }
605 };
606 }
607
608 pub fn run<A: ToSocketAddrs>(self, addr: A) {
610 run_server(self, addr, num_cpus::get());
611 }
612
613 pub fn run_threads<A: ToSocketAddrs>(self, addr: A, threads: usize) {
615 run_server(self, addr, threads);
616 }
617}
618
619impl hyper::server::Handler for Pencil {
620 fn handle(&self, req: HTTPRequest, mut res: HTTPResponse) {
621 debug!("Request: {}", req.uri);
622 match Request::new(self, req) {
623 Ok(mut request) => {
624 let response = self.handle_request(&mut request);
625 response.write(request.method(), res);
626 }
627 Err(_) => {
628 *res.status_mut() = StatusCode::BadRequest;
629 if let Ok(w) = res.start() {
630 let _ = w.end();
631 }
632 }
633 };
634 }
635}
636
637impl PathBound for Pencil {
638 fn open_resource(&self, resource: &str) -> File {
639 let mut pathbuf = PathBuf::from(&self.root_path);
640 pathbuf.push(resource);
641 File::open(&pathbuf.as_path()).unwrap()
642 }
643}
644
645impl fmt::Display for Pencil {
646 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
647 write!(f, "<Pencil application {}>", self.name)
648 }
649}
650
651impl fmt::Debug for Pencil {
652 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
653 write!(f, "<Pencil application {}>", self.name)
654 }
655}
656
657fn send_app_static_file(request: &mut Request) -> PencilResult {
660 let mut static_path = PathBuf::from(&request.app.root_path);
661 static_path.push(&request.app.static_folder);
662 let static_path_str = static_path.to_str().unwrap();
663 let filename = request.view_args.get("filename").unwrap();
664 send_from_directory_range(static_path_str, filename, false, request.headers().get())
665}
666
667
668
669fn check_if_cached(req: &mut Request) -> Option<PencilResult> {
670
671 let mod_time = req.app.extensions.get::<TimeAtServerStartKey>().expect("TimeAtServerStartKey should've been set up.");
672
673 match req.headers().get::<IfModifiedSince>() {
674 Some(&IfModifiedSince(HttpDate(tm))) if tm >= *mod_time => {
675 let mut cached_resp = Response::new_empty();
676 cached_resp.status_code = 304;
677 return Some(Ok(cached_resp));
678 },
679 None => { return None;
681 },
682 Some(_) => { return None;
684 }
685 }
686}
687
688struct MaxAgeKey;
689
690impl Key for MaxAgeKey {
691 type Value = ::std::time::Duration;
692}
693
694struct TimeAtServerStartKey;
695
696impl Key for TimeAtServerStartKey {
697 type Value = time::Tm;
698}
699
700fn send_app_static_file_with_cache(request: &mut Request) -> PencilResult {
703 if let Some(resp) = check_if_cached(request) {
704 return resp;
705 }
706 let mut static_path = PathBuf::from(&request.app.root_path);
707 static_path.push(&request.app.static_folder);
708 let static_path_str = static_path.to_str().unwrap();
709 let filename = request.view_args.get("filename").unwrap();
710 let resp = send_from_directory_range(static_path_str, filename, false, request.headers().get());
711 resp.map(|mut r| {
712 let mod_time = request.app.extensions.get::<TimeAtServerStartKey>().expect("TimeAtServerStartKey should've been set up.");
713 r.headers.set(LastModified(HttpDate(*mod_time)));
714 let max_age = request.app.extensions.get::<MaxAgeKey>().expect("MaxAgeKey should've been set up.").as_secs();
715 if max_age > 0 {
716 r.headers.set(CacheControl(vec![CacheDirective::MaxAge(max_age as u32)]));
717 }
718 r
719 })
720}
721