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-10-03 10:39:56 +00:00
|
|
|
use std::process;
|
2016-08-24 08:30:09 +00:00
|
|
|
|
2016-04-01 23:54:53 +00:00
|
|
|
use term_painter::Color::*;
|
|
|
|
use term_painter::ToStyle;
|
|
|
|
|
2016-10-04 02:21:21 +00:00
|
|
|
use config;
|
2016-10-04 02:37:49 +00:00
|
|
|
use logger;
|
2016-10-08 06:20:49 +00:00
|
|
|
use request::{Request, Data, FormItems};
|
2016-10-16 07:03:08 +00:00
|
|
|
use response::Responder;
|
2016-10-04 00:09:13 +00:00
|
|
|
use router::{Router, Route};
|
|
|
|
use catcher::{self, Catcher};
|
2016-10-08 02:27:26 +00:00
|
|
|
use outcome::Outcome;
|
2016-10-04 00:09:13 +00:00
|
|
|
use error::Error;
|
|
|
|
|
2016-10-08 06:20:49 +00:00
|
|
|
use http::{Method, StatusCode};
|
2016-10-04 00:09:13 +00:00
|
|
|
use http::hyper::{HyperRequest, FreshHyperResponse};
|
|
|
|
use http::hyper::{HyperServer, HyperHandler, HyperSetCookie, header};
|
2016-04-01 23:54:53 +00:00
|
|
|
|
|
|
|
pub struct Rocket {
|
2016-09-08 07:25:40 +00:00
|
|
|
address: String,
|
2016-10-03 10:39:56 +00:00
|
|
|
port: usize,
|
2016-04-06 10:26:43 +00:00
|
|
|
router: Router,
|
2016-10-08 06:20:49 +00:00
|
|
|
default_catchers: HashMap<u16, Catcher>,
|
2016-04-06 20:50:02 +00:00
|
|
|
catchers: HashMap<u16, Catcher>,
|
2016-04-11 10:57:23 +00:00
|
|
|
}
|
|
|
|
|
2016-10-08 11:42:22 +00:00
|
|
|
#[doc(hidden)]
|
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,
|
2016-10-16 07:03:08 +00:00
|
|
|
hyp_req: HyperRequest<'h, 'k>,
|
2016-09-30 22:20:11 +00:00
|
|
|
mut res: FreshHyperResponse<'h>) {
|
2016-10-09 04:37:28 +00:00
|
|
|
// Get all of the information from Hyper.
|
2016-10-09 11:29:02 +00:00
|
|
|
let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();
|
2016-10-09 04:37:28 +00:00
|
|
|
|
2016-10-16 07:03:08 +00:00
|
|
|
// Get a copy of the URI for later use.
|
|
|
|
let uri = h_uri.to_string();
|
|
|
|
|
2016-10-09 04:37:28 +00:00
|
|
|
// Try to create a Rocket request from the hyper request info.
|
2016-10-09 11:29:02 +00:00
|
|
|
let mut request = match Request::new(h_method, h_headers, h_uri) {
|
|
|
|
Ok(req) => req,
|
2016-09-22 01:23:44 +00:00
|
|
|
Err(ref reason) => {
|
2016-10-16 10:16:16 +00:00
|
|
|
let mock = Request::mock(Method::Get, uri.as_str());
|
|
|
|
error!("{}: bad request ({}).", mock, reason);
|
|
|
|
self.handle_error(StatusCode::InternalServerError, &mock, res);
|
|
|
|
return;
|
2016-08-27 01:37:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-10-16 07:03:08 +00:00
|
|
|
// Retrieve the data from the hyper body.
|
|
|
|
let data = match Data::from_hyp(h_body) {
|
2016-10-09 11:29:02 +00:00
|
|
|
Ok(data) => data,
|
|
|
|
Err(reason) => {
|
2016-10-16 07:03:08 +00:00
|
|
|
error_!("Bad data in request: {}", reason);
|
2016-10-16 10:16:16 +00:00
|
|
|
self.handle_error(StatusCode::InternalServerError, &request, res);
|
|
|
|
return;
|
2016-10-09 11:29:02 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-10-16 07:03:08 +00:00
|
|
|
// Set the common response headers and preprocess the request.
|
|
|
|
res.headers_mut().set(header::Server("rocket".to_string()));
|
2016-10-09 11:29:02 +00:00
|
|
|
self.preprocess_request(&mut request, &data);
|
2016-10-08 06:20:49 +00:00
|
|
|
|
2016-10-16 07:03:08 +00:00
|
|
|
// Now that we've Rocket-ized everything, actually dispath the request.
|
|
|
|
let mut responder = match self.dispatch(&request, data) {
|
|
|
|
Ok(responder) => responder,
|
2016-10-16 10:16:16 +00:00
|
|
|
Err(code) => {
|
|
|
|
self.handle_error(code, &request, res);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-16 07:03:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// We have a responder. Update the cookies in the header.
|
|
|
|
let cookie_delta = request.cookies().delta();
|
|
|
|
if cookie_delta.len() > 0 {
|
|
|
|
res.headers_mut().set(HyperSetCookie(cookie_delta));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actually call the responder.
|
|
|
|
let outcome = responder.respond(res);
|
|
|
|
info_!("{} {}", White.paint("Outcome:"), outcome);
|
|
|
|
|
|
|
|
// Check if the responder wants to forward to a catcher. If it doesn't,
|
|
|
|
// it's a success or failure, so we can't do any more processing.
|
|
|
|
if let Some((code, f_res)) = outcome.forwarded() {
|
2016-10-16 10:16:16 +00:00
|
|
|
self.handle_error(code, &request, f_res);
|
2016-10-16 07:03:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rocket {
|
2016-10-16 10:21:34 +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, data: &Data) {
|
|
|
|
// 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 = data.peek().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(&data.peek()[..min(data_len, max_len)])
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut form_items = FormItems(form);
|
|
|
|
if let Some(("_method", value)) = form_items.next() {
|
|
|
|
if let Ok(method) = value.parse() {
|
|
|
|
req.method = method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-16 10:16:16 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn dispatch<'r>(&self, request: &'r Request, mut data: Data)
|
2016-10-16 07:03:08 +00:00
|
|
|
-> Result<Box<Responder + 'r>, StatusCode> {
|
|
|
|
// Go through the list of matching routes until we fail or succeed.
|
2016-10-16 10:16:16 +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-10-08 06:20:49 +00:00
|
|
|
// Dispatch the request to the handler.
|
|
|
|
let response = (route.handler)(&request, data);
|
|
|
|
|
|
|
|
// Check if the request processing completed or if the request needs
|
|
|
|
// to be forwarded. If it does, continue the loop to try again.
|
|
|
|
info_!("{} {}", White.paint("Response:"), response);
|
2016-10-16 07:03:08 +00:00
|
|
|
match response {
|
|
|
|
Outcome::Success(responder) => return Ok(responder),
|
|
|
|
Outcome::Failure(status_code) => return Err(status_code),
|
|
|
|
Outcome::Forward(unused_data) => data = unused_data,
|
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-10-16 07:03:08 +00:00
|
|
|
Err(StatusCode::NotFound)
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
2016-04-11 10:57:23 +00:00
|
|
|
|
2016-10-16 10:16:16 +00:00
|
|
|
// Call when no route was found. Returns true if there was a response.
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn handle_error<'r>(&self,
|
2016-10-08 06:20:49 +00:00
|
|
|
code: StatusCode,
|
|
|
|
req: &'r Request,
|
2016-10-16 10:16:16 +00:00
|
|
|
response: FreshHyperResponse) -> bool {
|
2016-10-09 11:29:02 +00:00
|
|
|
// Find the catcher or use the one for internal server errors.
|
|
|
|
let catcher = self.catchers.get(&code.to_u16()).unwrap_or_else(|| {
|
|
|
|
error_!("No catcher found for {}.", code);
|
|
|
|
warn_!("Using internal server error catcher.");
|
|
|
|
self.catchers.get(&500).expect("500 Catcher")
|
|
|
|
});
|
2016-10-08 06:20:49 +00:00
|
|
|
|
|
|
|
if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
|
2016-10-14 01:39:23 +00:00
|
|
|
if !responder.respond(response).is_success() {
|
2016-10-08 06:37:29 +00:00
|
|
|
error_!("Catcher outcome was unsuccessul; aborting response.");
|
2016-10-16 10:16:16 +00:00
|
|
|
return false;
|
2016-10-08 06:37:29 +00:00
|
|
|
} else {
|
2016-10-15 01:57:36 +00:00
|
|
|
info_!("Responded with {} catcher.", White.paint(code));
|
2016-10-08 06:20:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-10-08 06:37:29 +00:00
|
|
|
error_!("Catcher returned an incomplete response.");
|
|
|
|
warn_!("Using default error response.");
|
2016-10-09 11:29:02 +00:00
|
|
|
let catcher = self.default_catchers.get(&code.to_u16())
|
|
|
|
.unwrap_or(self.default_catchers.get(&500).expect("500 default"));
|
2016-10-08 06:20:49 +00:00
|
|
|
let responder = catcher.handle(Error::Internal, req).responder();
|
2016-10-14 01:39:23 +00:00
|
|
|
responder.unwrap().respond(response).unwrap()
|
2016-10-08 06:20:49 +00:00
|
|
|
}
|
2016-10-16 10:16:16 +00:00
|
|
|
|
|
|
|
true
|
2016-04-11 10:57:23 +00:00
|
|
|
}
|
|
|
|
|
2016-10-08 11:42:22 +00:00
|
|
|
pub fn mount(mut self, base: &str, routes: Vec<Route>) -> Self {
|
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 {
|
2016-10-18 02:29:58 +00:00
|
|
|
let path = format!("{}/{}", base, route.path.as_uri());
|
2016-04-02 07:51:40 +00:00
|
|
|
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-10-04 02:48:33 +00:00
|
|
|
pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
|
2016-08-24 08:30:09 +00:00
|
|
|
info!("👾 {}:", Magenta.paint("Catchers"));
|
2016-04-06 20:50:02 +00:00
|
|
|
for c in catchers {
|
2016-10-12 07:14:42 +00:00
|
|
|
if self.catchers.get(&c.code).map_or(false, |e| !e.is_default()) {
|
|
|
|
let msg = "(warning: duplicate catcher!)";
|
|
|
|
info_!("{} {}", c, Yellow.paint(msg));
|
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-10-04 02:37:49 +00:00
|
|
|
pub fn launch(self) {
|
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-10-03 10:39:56 +00:00
|
|
|
let server = match HyperServer::http(full_addr.as_str()) {
|
|
|
|
Ok(hyper_server) => hyper_server,
|
|
|
|
Err(e) => {
|
|
|
|
error!("failed to start server.");
|
|
|
|
error_!("{}", e);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-09-30 22:20:11 +00:00
|
|
|
info!("🚀 {} {}...",
|
|
|
|
White.paint("Rocket has launched from"),
|
|
|
|
White.bold().paint(&full_addr));
|
2016-10-03 10:39:56 +00:00
|
|
|
|
|
|
|
server.handle(self).unwrap();
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|
2016-04-03 10:36:30 +00:00
|
|
|
|
2016-10-03 10:39:56 +00:00
|
|
|
pub fn ignite() -> Rocket {
|
2016-10-15 01:57:36 +00:00
|
|
|
// Note: init() will exit the process under config errors.
|
2016-10-16 10:16:16 +00:00
|
|
|
let (config, initted) = config::init();
|
|
|
|
if initted {
|
|
|
|
logger::init(config.log_level);
|
|
|
|
}
|
2016-10-03 10:39:56 +00:00
|
|
|
|
2016-10-15 01:57:36 +00:00
|
|
|
info!("🔧 Configured for {}.", config.env);
|
2016-10-03 10:39:56 +00:00
|
|
|
info_!("listening: {}:{}",
|
2016-10-15 01:57:36 +00:00
|
|
|
White.paint(&config.address),
|
|
|
|
White.paint(&config.port));
|
|
|
|
info_!("logging: {:?}", White.paint(config.log_level));
|
2016-10-16 01:58:57 +00:00
|
|
|
info_!("session key: {}", White.paint(config.take_session_key().is_some()));
|
2016-10-15 01:57:36 +00:00
|
|
|
for (name, value) in config.extras() {
|
|
|
|
info_!("{} {}: {}", Yellow.paint("[extra]"), name, White.paint(value));
|
|
|
|
}
|
2016-10-03 10:39:56 +00:00
|
|
|
|
|
|
|
Rocket {
|
2016-10-15 01:57:36 +00:00
|
|
|
address: config.address.clone(),
|
|
|
|
port: config.port,
|
2016-10-03 10:39:56 +00:00
|
|
|
router: Router::new(),
|
2016-10-08 06:20:49 +00:00
|
|
|
default_catchers: catcher::defaults::get(),
|
2016-10-03 10:39:56 +00:00
|
|
|
catchers: catcher::defaults::get(),
|
|
|
|
}
|
|
|
|
}
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|