2016-04-01 23:54:53 +00:00
|
|
|
use super::*;
|
2016-08-27 04:34:28 +00:00
|
|
|
use response::{FreshHyperResponse, Outcome};
|
2016-04-01 23:54:53 +00:00
|
|
|
use request::HyperRequest;
|
2016-04-06 20:50:02 +00:00
|
|
|
use catcher;
|
2016-04-01 23:54:53 +00:00
|
|
|
|
2016-04-06 20:50:02 +00:00
|
|
|
use std::collections::HashMap;
|
2016-09-25 09:26:15 +00:00
|
|
|
use std::str::from_utf8_unchecked;
|
|
|
|
use std::cmp::min;
|
2016-08-24 08:30:09 +00:00
|
|
|
|
2016-04-01 23:54:53 +00:00
|
|
|
use term_painter::Color::*;
|
|
|
|
use term_painter::ToStyle;
|
|
|
|
|
|
|
|
use hyper::server::Server as HyperServer;
|
|
|
|
use hyper::server::Handler as HyperHandler;
|
2016-09-12 01:57:04 +00:00
|
|
|
use hyper::header::SetCookie;
|
2016-04-01 23:54:53 +00:00
|
|
|
|
|
|
|
pub struct Rocket {
|
2016-09-08 07:25:40 +00:00
|
|
|
address: String,
|
2016-04-01 23:54:53 +00:00
|
|
|
port: isize,
|
2016-04-06 10:26:43 +00:00
|
|
|
router: Router,
|
2016-04-06 20:50:02 +00:00
|
|
|
catchers: HashMap<u16, Catcher>,
|
2016-08-27 01:37:28 +00:00
|
|
|
log_set: bool,
|
2016-04-11 10:57:23 +00:00
|
|
|
}
|
|
|
|
|
2016-04-01 23:54:53 +00:00
|
|
|
impl HyperHandler for Rocket {
|
2016-09-30 22:20:11 +00:00
|
|
|
fn handle<'h, 'k>(&self,
|
|
|
|
req: HyperRequest<'h, 'k>,
|
|
|
|
mut res: FreshHyperResponse<'h>) {
|
2016-08-02 02:07:36 +00:00
|
|
|
res.headers_mut().set(response::header::Server("rocket".to_string()));
|
2016-04-23 02:48:03 +00:00
|
|
|
self.dispatch(req, res)
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rocket {
|
2016-09-30 22:20:11 +00:00
|
|
|
fn dispatch<'h, 'k>(&self,
|
|
|
|
hyp_req: HyperRequest<'h, 'k>,
|
2016-08-27 04:34:28 +00:00
|
|
|
mut res: FreshHyperResponse<'h>) {
|
2016-08-27 01:37:28 +00:00
|
|
|
// Get a copy of the URI for later use.
|
|
|
|
let uri = hyp_req.uri.to_string();
|
|
|
|
|
|
|
|
// Try to create a Rocket request from the hyper request.
|
|
|
|
let request = match Request::from_hyp(hyp_req) {
|
2016-09-25 09:26:15 +00:00
|
|
|
Ok(mut req) => {
|
|
|
|
self.preprocess_request(&mut req);
|
|
|
|
req
|
2016-09-30 22:20:11 +00:00
|
|
|
}
|
2016-09-22 01:23:44 +00:00
|
|
|
Err(ref reason) => {
|
2016-08-27 01:37:28 +00:00
|
|
|
let mock_request = Request::mock(Method::Get, uri.as_str());
|
2016-09-22 11:12:07 +00:00
|
|
|
debug_!("Bad request: {}", reason);
|
|
|
|
return self.handle_internal_error(&mock_request, res);
|
2016-08-27 01:37:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
info!("{}:", request);
|
2016-08-27 04:34:28 +00:00
|
|
|
let matches = self.router.route(&request);
|
|
|
|
for route in matches {
|
2016-08-26 08:55:11 +00:00
|
|
|
// Retrieve and set the requests parameters.
|
2016-08-27 04:34:28 +00:00
|
|
|
info_!("Matched: {}", route);
|
2016-08-27 01:37:28 +00:00
|
|
|
request.set_params(route);
|
2016-08-26 08:55:11 +00:00
|
|
|
|
2016-09-12 01:57:04 +00:00
|
|
|
// Dispatch the request to the handler and update the cookies.
|
|
|
|
let mut responder = (route.handler)(&request);
|
2016-09-12 08:51:02 +00:00
|
|
|
let cookie_delta = request.cookies().delta();
|
|
|
|
if cookie_delta.len() > 0 {
|
|
|
|
res.headers_mut().set(SetCookie(cookie_delta));
|
|
|
|
}
|
2016-09-12 01:57:04 +00:00
|
|
|
|
|
|
|
// Get the response.
|
|
|
|
let outcome = responder.respond(res);
|
2016-08-26 08:55:11 +00:00
|
|
|
info_!("{} {}", White.paint("Outcome:"), outcome);
|
|
|
|
|
2016-09-22 01:23:44 +00:00
|
|
|
// Get the result if we failed forward so we can try again.
|
2016-08-27 04:34:28 +00:00
|
|
|
res = match outcome {
|
2016-09-22 01:31:26 +00:00
|
|
|
Outcome::Complete | Outcome::FailStop => return,
|
2016-08-27 05:05:33 +00:00
|
|
|
Outcome::FailForward(r) => r,
|
2016-09-30 22:20:11 +00:00
|
|
|
Outcome::Bad(r) => return self.handle_internal_error(&request, r),
|
2016-08-27 04:34:28 +00:00
|
|
|
};
|
2016-04-11 10:57:23 +00:00
|
|
|
}
|
2016-08-27 04:34:28 +00:00
|
|
|
|
2016-09-08 07:02:17 +00:00
|
|
|
error_!("No matching routes.");
|
2016-08-27 04:34:28 +00:00
|
|
|
self.handle_not_found(&request, res);
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
2016-04-11 10:57:23 +00:00
|
|
|
|
2016-09-25 09:26:15 +00:00
|
|
|
/// Preprocess the request for Rocket-specific things. At this time, we're
|
|
|
|
/// only checking for _method in forms.
|
|
|
|
fn preprocess_request(&self, req: &mut Request) {
|
|
|
|
// Check if this is a form and if the form contains the special _method
|
|
|
|
// field which we use to reinterpret the request's method.
|
|
|
|
let data_len = req.data.len();
|
|
|
|
let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
|
|
|
|
if req.content_type().is_form() && data_len >= min_len {
|
|
|
|
let form = unsafe {
|
|
|
|
from_utf8_unchecked(&req.data.as_slice()[..min(data_len, max_len)])
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut form_items = form::FormItems(form);
|
|
|
|
if let Some(("_method", value)) = form_items.next() {
|
|
|
|
if let Ok(method) = value.parse() {
|
|
|
|
req.method = method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-27 01:37:28 +00:00
|
|
|
// Call on internal server error.
|
2016-09-30 22:20:11 +00:00
|
|
|
fn handle_internal_error<'r>(&self,
|
|
|
|
request: &'r Request<'r>,
|
|
|
|
response: FreshHyperResponse) {
|
2016-09-22 11:12:07 +00:00
|
|
|
error_!("Internal server error.");
|
2016-08-27 01:37:28 +00:00
|
|
|
let catcher = self.catchers.get(&500).unwrap();
|
|
|
|
catcher.handle(Error::Internal, request).respond(response);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call when no route was found.
|
2016-09-30 22:20:11 +00:00
|
|
|
fn handle_not_found<'r>(&self,
|
|
|
|
request: &'r Request<'r>,
|
2016-08-26 08:55:11 +00:00
|
|
|
response: FreshHyperResponse) {
|
2016-08-27 01:37:28 +00:00
|
|
|
error_!("{} dispatch failed: 404.", request);
|
2016-08-26 08:55:11 +00:00
|
|
|
let catcher = self.catchers.get(&404).unwrap();
|
|
|
|
catcher.handle(Error::NoRoute, request).respond(response);
|
2016-04-11 10:57:23 +00:00
|
|
|
}
|
|
|
|
|
2016-09-08 07:25:40 +00:00
|
|
|
pub fn new<S: ToString>(address: S, port: isize) -> Rocket {
|
2016-04-01 23:54:53 +00:00
|
|
|
Rocket {
|
2016-09-08 07:25:40 +00:00
|
|
|
address: address.to_string(),
|
2016-04-01 23:54:53 +00:00
|
|
|
port: port,
|
2016-04-06 10:26:43 +00:00
|
|
|
router: Router::new(),
|
2016-04-06 20:50:02 +00:00
|
|
|
catchers: catcher::defaults::get(),
|
2016-08-27 01:37:28 +00:00
|
|
|
log_set: false,
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 22:20:11 +00:00
|
|
|
pub fn mount(&mut self, base: &'static str, routes: Vec<Route>) -> &mut Self {
|
2016-08-27 04:34:28 +00:00
|
|
|
self.enable_normal_logging_if_disabled();
|
2016-08-24 08:30:09 +00:00
|
|
|
info!("🛰 {} '{}':", Magenta.paint("Mounting"), base);
|
2016-04-02 07:51:40 +00:00
|
|
|
for mut route in routes {
|
|
|
|
let path = format!("{}/{}", base, route.path.as_str());
|
|
|
|
route.set_path(path);
|
|
|
|
|
2016-08-24 08:30:09 +00:00
|
|
|
info_!("{}", route);
|
2016-04-02 07:51:40 +00:00
|
|
|
self.router.add(route);
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-04-06 10:26:43 +00:00
|
|
|
pub fn catch(&mut self, catchers: Vec<Catcher>) -> &mut Self {
|
2016-08-27 04:34:28 +00:00
|
|
|
self.enable_normal_logging_if_disabled();
|
2016-08-24 08:30:09 +00:00
|
|
|
info!("👾 {}:", Magenta.paint("Catchers"));
|
2016-04-06 20:50:02 +00:00
|
|
|
for c in catchers {
|
|
|
|
if self.catchers.contains_key(&c.code) &&
|
|
|
|
!self.catchers.get(&c.code).unwrap().is_default() {
|
|
|
|
let msg = format!("warning: overrides {} catcher!", c.code);
|
2016-08-27 04:34:28 +00:00
|
|
|
warn!("{} ({})", c, Yellow.paint(msg.as_str()));
|
2016-04-06 20:50:02 +00:00
|
|
|
} else {
|
2016-08-24 08:30:09 +00:00
|
|
|
info_!("{}", c);
|
2016-04-06 20:50:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.catchers.insert(c.code, c);
|
2016-04-06 10:26:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-08-27 04:34:28 +00:00
|
|
|
fn enable_normal_logging_if_disabled(&mut self) {
|
|
|
|
if !self.log_set {
|
|
|
|
logger::init(LoggingLevel::Normal);
|
|
|
|
self.log_set = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-27 01:37:28 +00:00
|
|
|
pub fn log(&mut self, level: LoggingLevel) {
|
|
|
|
if self.log_set {
|
|
|
|
warn!("Log level already set! Not overriding.");
|
|
|
|
} else {
|
|
|
|
logger::init(level);
|
|
|
|
self.log_set = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-22 01:23:44 +00:00
|
|
|
/// Retrieves the configuration parameter named `name` for the current
|
|
|
|
/// environment. Returns Some(value) if the paremeter exists. Otherwise,
|
|
|
|
/// returns None.
|
|
|
|
pub fn config<S: AsRef<str>>(_name: S) -> Option<&'static str> {
|
|
|
|
// TODO: Implement me.
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2016-08-27 01:37:28 +00:00
|
|
|
pub fn launch(mut self) {
|
2016-08-27 04:34:28 +00:00
|
|
|
self.enable_normal_logging_if_disabled();
|
2016-04-01 23:54:53 +00:00
|
|
|
if self.router.has_collisions() {
|
2016-08-24 08:30:09 +00:00
|
|
|
warn!("Route collisions detected!");
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let full_addr = format!("{}:{}", self.address, self.port);
|
2016-09-30 22:20:11 +00:00
|
|
|
info!("🚀 {} {}...",
|
|
|
|
White.paint("Rocket has launched from"),
|
|
|
|
White.bold().paint(&full_addr));
|
2016-04-01 23:54:53 +00:00
|
|
|
let _ = HyperServer::http(full_addr.as_str()).unwrap().handle(self);
|
|
|
|
}
|
2016-04-03 10:36:30 +00:00
|
|
|
|
|
|
|
pub fn mount_and_launch(mut self, base: &'static str, routes: Vec<Route>) {
|
|
|
|
self.mount(base, routes);
|
|
|
|
self.launch();
|
|
|
|
}
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|