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-12-15 08:47:31 +00:00
|
|
|
use std::io::{self, Write};
|
2017-04-20 20:43:01 +00:00
|
|
|
use std::mem;
|
2016-08-24 08:30:09 +00:00
|
|
|
|
2017-06-02 04:44:31 +00:00
|
|
|
use yansi::Paint;
|
2017-01-21 03:31:46 +00:00
|
|
|
use state::Container;
|
|
|
|
|
2017-07-12 11:13:46 +00:00
|
|
|
#[cfg(feature = "tls")] use hyper_sync_rustls::TlsServer;
|
2016-12-16 13:17:16 +00:00
|
|
|
use {logger, handler};
|
2017-05-15 04:46:01 +00:00
|
|
|
use ext::ReadExt;
|
2017-04-17 07:34:28 +00:00
|
|
|
use config::{self, Config, LoggedValue};
|
2016-10-25 11:24:07 +00:00
|
|
|
use request::{Request, FormItems};
|
|
|
|
use data::Data;
|
2016-12-15 08:47:31 +00:00
|
|
|
use response::{Body, Response};
|
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;
|
2017-04-19 00:42:44 +00:00
|
|
|
use error::{Error, LaunchError, LaunchErrorKind};
|
2017-04-20 20:43:01 +00:00
|
|
|
use fairing::{Fairing, Fairings};
|
2016-10-04 00:09:13 +00:00
|
|
|
|
2017-06-06 20:41:04 +00:00
|
|
|
use http::{Method, Status, Header};
|
2016-12-15 17:24:29 +00:00
|
|
|
use http::hyper::{self, header};
|
2017-09-13 09:12:48 +00:00
|
|
|
use http::uri::Uri;
|
2016-04-01 23:54:53 +00:00
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
/// The main `Rocket` type: used to mount routes and catchers and launch the
|
2016-10-21 09:56:57 +00:00
|
|
|
/// application.
|
2016-04-01 23:54:53 +00:00
|
|
|
pub struct Rocket {
|
2017-06-06 20:41:04 +00:00
|
|
|
pub(crate) config: Config,
|
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>,
|
2017-06-06 20:41:04 +00:00
|
|
|
pub(crate) state: Container,
|
|
|
|
fairings: Fairings,
|
2016-04-11 10:57:23 +00:00
|
|
|
}
|
|
|
|
|
2016-10-08 11:42:22 +00:00
|
|
|
#[doc(hidden)]
|
2016-12-15 17:24:29 +00:00
|
|
|
impl hyper::Handler for Rocket {
|
2016-11-04 13:35:04 +00:00
|
|
|
// This function tries to hide all of the Hyper-ness from Rocket. It
|
|
|
|
// essentially converts Hyper types into Rocket types, then calls the
|
|
|
|
// `dispatch` function, which knows nothing about Hyper. Because responding
|
|
|
|
// depends on the `HyperResponse` type, this function does the actual
|
|
|
|
// response processing.
|
2017-09-05 03:11:10 +00:00
|
|
|
fn handle<'h, 'k>(
|
|
|
|
&self,
|
|
|
|
hyp_req: hyper::Request<'h, 'k>,
|
|
|
|
res: hyper::FreshResponse<'h>,
|
|
|
|
) {
|
2016-10-09 04:37:28 +00:00
|
|
|
// Get all of the information from Hyper.
|
2017-01-13 15:50:51 +00:00
|
|
|
let (h_addr, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();
|
2016-10-09 04:37:28 +00:00
|
|
|
|
2016-12-16 11:07:23 +00:00
|
|
|
// Convert the Hyper request into a Rocket request.
|
2017-06-06 20:41:04 +00:00
|
|
|
let req_res = Request::from_hyp(self, h_method, h_headers, h_uri, h_addr);
|
|
|
|
let mut req = match req_res {
|
2017-01-13 15:50:51 +00:00
|
|
|
Ok(req) => req,
|
2016-12-16 11:07:23 +00:00
|
|
|
Err(e) => {
|
|
|
|
error!("Bad incoming request: {}", e);
|
2017-09-13 09:12:48 +00:00
|
|
|
let dummy = Request::new(self, Method::Get, Uri::new("<unknown>"));
|
2017-12-28 03:06:57 +00:00
|
|
|
let r = self.handle_error(Status::BadRequest, &dummy);
|
2016-12-15 08:47:31 +00:00
|
|
|
return self.issue_response(r, res);
|
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);
|
2017-01-13 15:50:51 +00:00
|
|
|
let r = self.handle_error(Status::InternalServerError, &req);
|
2016-12-15 08:47:31 +00:00
|
|
|
return self.issue_response(r, res);
|
2016-10-09 11:29:02 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-12-16 13:17:16 +00:00
|
|
|
// Dispatch the request to get a response, then write that response out.
|
2017-01-13 15:50:51 +00:00
|
|
|
let response = self.dispatch(&mut req, data);
|
2016-12-17 17:18:30 +00:00
|
|
|
self.issue_response(response, res)
|
2016-10-16 07:03:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-13 07:18:31 +00:00
|
|
|
// This macro is a terrible hack to get around Hyper's Server<L> type. What we
|
|
|
|
// want is to use almost exactly the same launch code when we're serving over
|
|
|
|
// HTTPS as over HTTP. But Hyper forces two different types, so we can't use the
|
|
|
|
// same code, at least not trivially. These macros get around that by passing in
|
|
|
|
// the same code as a continuation in `$continue`. This wouldn't work as a
|
|
|
|
// regular function taking in a closure because the types of the inputs to the
|
|
|
|
// closure would be different depending on whether TLS was enabled or not.
|
|
|
|
#[cfg(not(feature = "tls"))]
|
|
|
|
macro_rules! serve {
|
|
|
|
($rocket:expr, $addr:expr, |$server:ident, $proto:ident| $continue:expr) => ({
|
|
|
|
let ($proto, $server) = ("http://", hyper::Server::http($addr));
|
|
|
|
$continue
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "tls")]
|
|
|
|
macro_rules! serve {
|
|
|
|
($rocket:expr, $addr:expr, |$server:ident, $proto:ident| $continue:expr) => ({
|
2017-05-19 10:29:08 +00:00
|
|
|
if let Some(tls) = $rocket.config.tls.clone() {
|
|
|
|
let tls = TlsServer::new(tls.certs, tls.key);
|
2017-04-13 07:18:31 +00:00
|
|
|
let ($proto, $server) = ("https://", hyper::Server::https($addr, tls));
|
|
|
|
$continue
|
|
|
|
} else {
|
|
|
|
let ($proto, $server) = ("http://", hyper::Server::http($addr));
|
|
|
|
$continue
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-10-16 07:03:08 +00:00
|
|
|
impl Rocket {
|
2016-12-15 08:47:31 +00:00
|
|
|
#[inline]
|
2017-04-20 20:43:01 +00:00
|
|
|
fn issue_response(&self, response: Response, hyp_res: hyper::FreshResponse) {
|
2016-12-15 08:47:31 +00:00
|
|
|
match self.write_response(response, hyp_res) {
|
2017-06-02 04:44:31 +00:00
|
|
|
Ok(_) => info_!("{}", Paint::green("Response succeeded.")),
|
2017-09-05 03:11:10 +00:00
|
|
|
Err(e) => error_!("Failed to write response: {:?}.", e),
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-24 08:33:00 +00:00
|
|
|
#[inline]
|
2017-09-05 03:11:10 +00:00
|
|
|
fn write_response(
|
|
|
|
&self,
|
|
|
|
mut response: Response,
|
|
|
|
mut hyp_res: hyper::FreshResponse,
|
|
|
|
) -> io::Result<()> {
|
2016-12-15 08:47:31 +00:00
|
|
|
*hyp_res.status_mut() = hyper::StatusCode::from_u16(response.status().code);
|
|
|
|
|
2017-04-14 08:21:06 +00:00
|
|
|
for header in response.headers().iter() {
|
2017-02-22 19:25:30 +00:00
|
|
|
// FIXME: Using hyper here requires two allocations.
|
2017-01-03 03:33:36 +00:00
|
|
|
let name = header.name.into_string();
|
2017-02-22 19:25:30 +00:00
|
|
|
let value = Vec::from(header.value.as_bytes());
|
|
|
|
hyp_res.headers_mut().append_raw(name, value);
|
2016-12-15 08:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if response.body().is_none() {
|
|
|
|
hyp_res.headers_mut().set(header::ContentLength(0));
|
|
|
|
return hyp_res.start()?.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
match response.body() {
|
|
|
|
None => {
|
|
|
|
hyp_res.headers_mut().set(header::ContentLength(0));
|
|
|
|
hyp_res.start()?.end()
|
|
|
|
}
|
2017-08-11 16:14:54 +00:00
|
|
|
Some(Body::Sized(body, size)) => {
|
2016-12-15 08:47:31 +00:00
|
|
|
hyp_res.headers_mut().set(header::ContentLength(size));
|
|
|
|
let mut stream = hyp_res.start()?;
|
|
|
|
io::copy(body, &mut stream)?;
|
|
|
|
stream.end()
|
|
|
|
}
|
|
|
|
Some(Body::Chunked(mut body, chunk_size)) => {
|
|
|
|
// This _might_ happen on a 32-bit machine!
|
|
|
|
if chunk_size > (usize::max_value() as u64) {
|
|
|
|
let msg = "chunk size exceeds limits of usize type";
|
|
|
|
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
// The buffer stores the current chunk being written out.
|
|
|
|
let mut buffer = vec![0; chunk_size as usize];
|
|
|
|
let mut stream = hyp_res.start()?;
|
|
|
|
loop {
|
|
|
|
match body.read_max(&mut buffer)? {
|
|
|
|
0 => break,
|
|
|
|
n => stream.write_all(&buffer[..n])?,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stream.end()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-13 15:50:51 +00:00
|
|
|
/// Preprocess the request for Rocket things. Currently, this means:
|
|
|
|
///
|
|
|
|
/// * Rewriting the method in the request if _method form field exists.
|
|
|
|
///
|
|
|
|
/// Keep this in-sync with derive_form when preprocessing form fields.
|
2016-10-16 10:21:34 +00:00
|
|
|
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());
|
2017-02-01 11:12:24 +00:00
|
|
|
let is_form = req.content_type().map_or(false, |ct| ct.is_form());
|
2017-01-13 15:50:51 +00:00
|
|
|
if is_form && req.method() == Method::Post && data_len >= min_len {
|
2017-04-20 20:43:01 +00:00
|
|
|
// We're only using this for comparison and throwing it away
|
|
|
|
// afterwards, so it doesn't matter if we have invalid UTF8.
|
2017-09-05 03:11:10 +00:00
|
|
|
let form =
|
|
|
|
unsafe { from_utf8_unchecked(&data.peek()[..min(data_len, max_len)]) };
|
2016-10-16 10:21:34 +00:00
|
|
|
|
2017-03-31 06:06:53 +00:00
|
|
|
if let Some((key, value)) = FormItems::from(form).next() {
|
|
|
|
if key == "_method" {
|
|
|
|
if let Ok(method) = value.parse() {
|
|
|
|
req.set_method(method);
|
|
|
|
}
|
2016-10-16 10:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-03 10:16:46 +00:00
|
|
|
#[inline]
|
2017-09-05 03:11:10 +00:00
|
|
|
pub(crate) fn dispatch<'s, 'r>(
|
|
|
|
&'s self,
|
|
|
|
request: &'r mut Request<'s>,
|
|
|
|
data: Data,
|
|
|
|
) -> Response<'r> {
|
2017-01-13 15:50:51 +00:00
|
|
|
info!("{}:", request);
|
|
|
|
|
2017-04-20 20:43:01 +00:00
|
|
|
// Do a bit of preprocessing before routing; run the attached fairings.
|
2016-12-16 13:17:16 +00:00
|
|
|
self.preprocess_request(request, &data);
|
2017-04-20 20:43:01 +00:00
|
|
|
self.fairings.handle_request(request, &data);
|
2016-12-16 13:17:16 +00:00
|
|
|
|
|
|
|
// Route the request to get a response.
|
2017-04-20 20:43:01 +00:00
|
|
|
let mut response = match self.route(request, data) {
|
2016-12-16 13:17:16 +00:00
|
|
|
Outcome::Success(mut response) => {
|
2017-06-06 20:41:04 +00:00
|
|
|
// A user's route responded! Set the cookies.
|
2017-01-27 07:08:15 +00:00
|
|
|
for cookie in request.cookies().delta() {
|
|
|
|
response.adjoin_header(cookie);
|
2016-12-16 13:17:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
response
|
|
|
|
}
|
|
|
|
Outcome::Forward(data) => {
|
|
|
|
// Rust thinks `request` is still borrowed here, but it's
|
|
|
|
// obviously not (data has nothing to do with it), so we
|
|
|
|
// convince it to give us another mutable reference.
|
2017-04-20 20:43:01 +00:00
|
|
|
// TODO: Use something that is well defined, like UnsafeCell.
|
|
|
|
// But that causes variance issues...so wait for NLL.
|
2017-09-05 03:11:10 +00:00
|
|
|
let request: &'r mut Request<'s> =
|
|
|
|
unsafe { (&mut *(request as *const _ as *mut _)) };
|
2016-12-16 13:17:16 +00:00
|
|
|
|
2017-04-20 20:43:01 +00:00
|
|
|
// There was no matching route.
|
2016-12-16 13:17:16 +00:00
|
|
|
if request.method() == Method::Head {
|
2017-06-02 04:44:31 +00:00
|
|
|
info_!("Autohandling {} request.", Paint::white("HEAD"));
|
2016-12-16 13:17:16 +00:00
|
|
|
request.set_method(Method::Get);
|
|
|
|
let mut response = self.dispatch(request, data);
|
|
|
|
response.strip_body();
|
|
|
|
response
|
|
|
|
} else {
|
|
|
|
self.handle_error(Status::NotFound, request)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Outcome::Failure(status) => self.handle_error(status, request),
|
2017-04-20 20:43:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Add the 'rocket' server header to the response and run fairings.
|
|
|
|
// TODO: If removing Hyper, write out `Date` header too.
|
|
|
|
response.set_header(Header::new("Server", "Rocket"));
|
|
|
|
self.fairings.handle_response(request, &mut response);
|
|
|
|
|
|
|
|
response
|
2016-12-16 13:17:16 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
/// Tries to find a `Responder` for a given `request`. It does this by
|
|
|
|
/// routing the request and calling the handler for each matching route
|
2017-01-06 06:42:24 +00:00
|
|
|
/// until one of the handlers returns success or failure, or there are no
|
|
|
|
/// additional routes to try (forward). The corresponding outcome for each
|
|
|
|
/// condition is returned.
|
2017-05-27 02:48:50 +00:00
|
|
|
//
|
2017-05-30 01:10:07 +00:00
|
|
|
// TODO: We _should_ be able to take an `&mut` here and mutate the request
|
2017-05-27 02:48:50 +00:00
|
|
|
// at any pointer _before_ we pass it to a handler as long as we drop the
|
|
|
|
// outcome. That should be safe. Since no mutable borrow can be held
|
|
|
|
// (ensuring `handler` takes an immutable borrow), any caller to `route`
|
|
|
|
// should be able to supply an `&mut` and retain an `&` after the call.
|
2017-02-03 10:16:46 +00:00
|
|
|
#[inline]
|
2017-09-05 03:11:10 +00:00
|
|
|
pub(crate) fn route<'s, 'r>(
|
|
|
|
&'s self,
|
|
|
|
request: &'r Request<'s>,
|
|
|
|
mut data: Data,
|
|
|
|
) -> handler::Outcome<'r> {
|
2016-10-16 07:03:08 +00:00
|
|
|
// Go through the list of matching routes until we fail or succeed.
|
2016-12-17 17:18:30 +00:00
|
|
|
let matches = self.router.route(request);
|
2016-08-27 04:34:28 +00:00
|
|
|
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);
|
2017-05-27 02:48:50 +00:00
|
|
|
request.set_route(route);
|
2016-08-26 08:55:11 +00:00
|
|
|
|
2016-10-08 06:20:49 +00:00
|
|
|
// Dispatch the request to the handler.
|
2016-12-17 17:18:30 +00:00
|
|
|
let outcome = (route.handler)(request, data);
|
2016-10-08 06:20:49 +00:00
|
|
|
|
|
|
|
// Check if the request processing completed or if the request needs
|
|
|
|
// to be forwarded. If it does, continue the loop to try again.
|
2017-06-02 04:44:31 +00:00
|
|
|
info_!("{} {}", Paint::white("Outcome:"), outcome);
|
2016-12-15 08:47:31 +00:00
|
|
|
match outcome {
|
2017-09-05 03:11:10 +00:00
|
|
|
o@Outcome::Success(_) | o@Outcome::Failure(_) => return o,
|
2016-10-16 07:03:08 +00:00
|
|
|
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-12-15 08:47:31 +00:00
|
|
|
error_!("No matching routes for {}.", request);
|
2016-12-16 13:17:16 +00:00
|
|
|
Outcome::Forward(data)
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
2016-04-11 10:57:23 +00:00
|
|
|
|
2017-04-13 07:18:31 +00:00
|
|
|
// Finds the error catcher for the status `status` and executes it fo the
|
|
|
|
// given request `req`. If a user has registere a catcher for `status`, the
|
|
|
|
// catcher is called. If the catcher fails to return a good response, the
|
|
|
|
// 500 catcher is executed. if there is no registered catcher for `status`,
|
|
|
|
// the default catcher is used.
|
2017-02-03 10:16:46 +00:00
|
|
|
fn handle_error<'r>(&self, status: Status, req: &'r Request) -> Response<'r> {
|
2017-06-02 04:44:31 +00:00
|
|
|
warn_!("Responding with {} catcher.", Paint::red(&status));
|
2016-10-08 06:20:49 +00:00
|
|
|
|
2016-12-15 08:47:31 +00:00
|
|
|
// Try to get the active catcher but fallback to user's 500 catcher.
|
|
|
|
let catcher = self.catchers.get(&status.code).unwrap_or_else(|| {
|
|
|
|
error_!("No catcher found for {}. Using 500 catcher.", status);
|
|
|
|
self.catchers.get(&500).expect("500 catcher.")
|
|
|
|
});
|
2016-10-16 10:16:16 +00:00
|
|
|
|
2016-12-15 08:47:31 +00:00
|
|
|
// Dispatch to the user's catcher. If it fails, use the default 500.
|
|
|
|
let error = Error::NoRoute;
|
|
|
|
catcher.handle(error, req).unwrap_or_else(|err_status| {
|
|
|
|
error_!("Catcher failed with status: {}!", err_status);
|
|
|
|
warn_!("Using default 500 error catcher.");
|
|
|
|
let default = self.default_catchers.get(&500).expect("Default 500");
|
|
|
|
default.handle(error, req).expect("Default 500 response.")
|
|
|
|
})
|
2016-04-11 10:57:23 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
/// Create a new `Rocket` application using the configuration information in
|
|
|
|
/// `Rocket.toml`. If the file does not exist or if there is an I/O error
|
|
|
|
/// reading the file, the defaults are used. See the
|
|
|
|
/// [config](/rocket/config/index.html) documentation for more information
|
|
|
|
/// on defaults.
|
|
|
|
///
|
|
|
|
/// This method is typically called through the `rocket::ignite` alias.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// If there is an error parsing the `Rocket.toml` file, this functions
|
|
|
|
/// prints a nice error message and then exits the process.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # {
|
|
|
|
/// rocket::ignite()
|
|
|
|
/// # };
|
|
|
|
/// ```
|
2017-05-19 10:29:08 +00:00
|
|
|
#[inline]
|
2016-11-04 13:35:04 +00:00
|
|
|
pub fn ignite() -> Rocket {
|
|
|
|
// Note: init() will exit the process under config errors.
|
2017-05-19 10:29:08 +00:00
|
|
|
Rocket::configured(config::init(), true)
|
2016-11-04 13:35:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new `Rocket` application using the supplied custom
|
2017-01-12 02:31:37 +00:00
|
|
|
/// configuration information. The `Rocket.toml` file, if present, is
|
2017-01-14 00:45:46 +00:00
|
|
|
/// ignored. Any environment variables setting config parameters are
|
2017-01-12 02:31:37 +00:00
|
|
|
/// ignored. If `log` is `true`, logging is enabled.
|
2016-11-04 13:35:04 +00:00
|
|
|
///
|
|
|
|
/// This method is typically called through the `rocket::custom` alias.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::config::{Config, Environment};
|
|
|
|
/// # use rocket::config::ConfigError;
|
|
|
|
///
|
2017-02-02 10:16:21 +00:00
|
|
|
/// # #[allow(dead_code)]
|
2016-11-04 13:35:04 +00:00
|
|
|
/// # fn try_config() -> Result<(), ConfigError> {
|
2017-01-12 02:31:37 +00:00
|
|
|
/// let config = Config::build(Environment::Staging)
|
|
|
|
/// .address("1.2.3.4")
|
|
|
|
/// .port(9234)
|
|
|
|
/// .finalize()?;
|
2016-11-04 13:35:04 +00:00
|
|
|
///
|
2017-02-02 10:16:21 +00:00
|
|
|
/// # #[allow(unused_variables)]
|
2017-01-12 02:31:37 +00:00
|
|
|
/// let app = rocket::custom(config, false);
|
2016-11-04 13:35:04 +00:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2017-05-19 10:29:08 +00:00
|
|
|
#[inline]
|
2017-01-12 02:31:37 +00:00
|
|
|
pub fn custom(config: Config, log: bool) -> Rocket {
|
2017-05-19 10:29:08 +00:00
|
|
|
Rocket::configured(config, log)
|
2017-01-12 02:31:37 +00:00
|
|
|
}
|
|
|
|
|
2017-05-19 10:29:08 +00:00
|
|
|
#[inline]
|
|
|
|
fn configured(config: Config, log: bool) -> Rocket {
|
2017-01-12 02:31:37 +00:00
|
|
|
if log {
|
2017-05-19 10:29:08 +00:00
|
|
|
logger::try_init(config.log_level, false);
|
2017-01-12 02:31:37 +00:00
|
|
|
}
|
|
|
|
|
2017-09-05 03:22:10 +00:00
|
|
|
info!("{}Configured for {}.", Paint::masked("🔧 "), config.environment);
|
2017-06-02 04:44:31 +00:00
|
|
|
info_!("address: {}", Paint::white(&config.address));
|
|
|
|
info_!("port: {}", Paint::white(&config.port));
|
|
|
|
info_!("log: {}", Paint::white(config.log_level));
|
|
|
|
info_!("workers: {}", Paint::white(config.workers));
|
2017-06-09 06:33:16 +00:00
|
|
|
info_!("secret key: {}", Paint::white(&config.secret_key));
|
2017-06-02 04:44:31 +00:00
|
|
|
info_!("limits: {}", Paint::white(&config.limits));
|
2016-12-22 09:29:58 +00:00
|
|
|
|
2017-04-13 07:18:31 +00:00
|
|
|
let tls_configured = config.tls.is_some();
|
|
|
|
if tls_configured && cfg!(feature = "tls") {
|
2017-06-02 04:44:31 +00:00
|
|
|
info_!("tls: {}", Paint::white("enabled"));
|
2017-06-09 06:33:16 +00:00
|
|
|
} else if tls_configured {
|
|
|
|
error_!("tls: {}", Paint::white("disabled"));
|
|
|
|
error_!("tls is configured, but the tls feature is disabled");
|
2017-04-13 07:18:31 +00:00
|
|
|
} else {
|
2017-06-09 06:33:16 +00:00
|
|
|
info_!("tls: {}", Paint::white("disabled"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.secret_key.is_generated() && config.environment.is_prod() {
|
|
|
|
warn!("environment is 'production', but no `secret_key` is configured");
|
2017-04-13 07:18:31 +00:00
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
for (name, value) in config.extras() {
|
2017-04-17 07:34:28 +00:00
|
|
|
info_!("{} {}: {}",
|
2017-08-19 01:37:25 +00:00
|
|
|
Paint::yellow("[extra]"),
|
|
|
|
Paint::blue(name),
|
|
|
|
Paint::white(LoggedValue(value)));
|
2016-11-04 13:35:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Rocket {
|
2017-01-12 02:31:37 +00:00
|
|
|
config: config,
|
2016-11-04 13:35:04 +00:00
|
|
|
router: Router::new(),
|
|
|
|
default_catchers: catcher::defaults::get(),
|
|
|
|
catchers: catcher::defaults::get(),
|
2017-04-20 20:43:01 +00:00
|
|
|
state: Container::new(),
|
2017-09-05 03:11:10 +00:00
|
|
|
fairings: Fairings::new(),
|
2016-11-04 13:35:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Mounts all of the routes in the supplied vector at the given `base`
|
|
|
|
/// path. Mounting a route with path `path` at path `base` makes the route
|
|
|
|
/// available at `base/path`.
|
|
|
|
///
|
2017-05-19 10:29:08 +00:00
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// The `base` mount point must be a static path. That is, the mount point
|
|
|
|
/// must _not_ contain dynamic path parameters: `<param>`.
|
|
|
|
///
|
2016-11-04 13:35:04 +00:00
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// Use the `routes!` macro to mount routes created using the code
|
|
|
|
/// generation facilities. Requests to the `/hello/world` URI will be
|
|
|
|
/// dispatched to the `hi` route.
|
|
|
|
///
|
|
|
|
/// ```rust
|
2017-08-29 03:14:59 +00:00
|
|
|
/// # #![feature(plugin, decl_macro)]
|
2016-11-04 13:35:04 +00:00
|
|
|
/// # #![plugin(rocket_codegen)]
|
|
|
|
/// # extern crate rocket;
|
|
|
|
/// #
|
|
|
|
/// #[get("/world")]
|
|
|
|
/// fn hi() -> &'static str {
|
|
|
|
/// "Hello!"
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// # if false { // We don't actually want to launch the server in an example.
|
|
|
|
/// rocket::ignite().mount("/hello", routes![hi])
|
2017-03-16 05:10:09 +00:00
|
|
|
/// # .launch();
|
2016-11-04 13:35:04 +00:00
|
|
|
/// # }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Manually create a route named `hi` at path `"/world"` mounted at base
|
|
|
|
/// `"/hello"`. Requests to the `/hello/world` URI will be dispatched to the
|
|
|
|
/// `hi` route.
|
|
|
|
///
|
|
|
|
/// ```rust
|
2016-12-15 08:47:31 +00:00
|
|
|
/// use rocket::{Request, Route, Data};
|
|
|
|
/// use rocket::handler::Outcome;
|
2016-11-04 13:35:04 +00:00
|
|
|
/// use rocket::http::Method::*;
|
|
|
|
///
|
2017-05-19 10:29:08 +00:00
|
|
|
/// fn hi(req: &Request, _: Data) -> Outcome<'static> {
|
|
|
|
/// Outcome::from(req, "Hello!")
|
2016-11-04 13:35:04 +00:00
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// # if false { // We don't actually want to launch the server in an example.
|
|
|
|
/// rocket::ignite().mount("/hello", vec![Route::new(Get, "/world", hi)])
|
2017-03-16 05:10:09 +00:00
|
|
|
/// # .launch();
|
2016-11-04 13:35:04 +00:00
|
|
|
/// # }
|
|
|
|
/// ```
|
2017-05-19 10:29:08 +00:00
|
|
|
#[inline]
|
2016-10-08 11:42:22 +00:00
|
|
|
pub fn mount(mut self, base: &str, routes: Vec<Route>) -> Self {
|
2017-09-05 03:22:10 +00:00
|
|
|
info!("{}{} '{}':",
|
|
|
|
Paint::masked("🛰 "),
|
|
|
|
Paint::purple("Mounting"),
|
|
|
|
Paint::blue(base));
|
2016-12-17 18:51:44 +00:00
|
|
|
|
2017-09-10 09:16:14 +00:00
|
|
|
if base.contains('<') || !base.starts_with('/') {
|
2016-12-17 18:51:44 +00:00
|
|
|
error_!("Bad mount point: '{}'.", base);
|
2017-09-11 08:14:44 +00:00
|
|
|
error_!("Mount points must be static, absolute URIs: `/example`");
|
2016-12-17 18:51:44 +00:00
|
|
|
panic!("Bad mount point.")
|
|
|
|
}
|
|
|
|
|
2016-04-02 07:51:40 +00:00
|
|
|
for mut route in routes {
|
2017-09-13 09:12:48 +00:00
|
|
|
let uri = Uri::new(format!("{}/{}", base, route.uri));
|
2017-06-12 22:08:34 +00:00
|
|
|
|
2017-05-27 02:48:50 +00:00
|
|
|
route.set_base(base);
|
2017-06-12 22:08:34 +00:00
|
|
|
route.set_uri(uri.to_string());
|
2016-04-02 07:51:40 +00:00
|
|
|
|
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-11-04 13:35:04 +00:00
|
|
|
/// Registers all of the catchers in the supplied vector.
|
2016-11-05 18:35:21 +00:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
2017-08-29 03:14:59 +00:00
|
|
|
/// #![feature(plugin, decl_macro)]
|
2016-11-05 18:35:21 +00:00
|
|
|
/// #![plugin(rocket_codegen)]
|
|
|
|
///
|
|
|
|
/// extern crate rocket;
|
|
|
|
///
|
|
|
|
/// use rocket::Request;
|
|
|
|
///
|
2017-09-23 02:04:14 +00:00
|
|
|
/// #[catch(500)]
|
2016-11-05 18:35:21 +00:00
|
|
|
/// fn internal_error() -> &'static str {
|
|
|
|
/// "Whoops! Looks like we messed up."
|
|
|
|
/// }
|
|
|
|
///
|
2017-09-23 02:04:14 +00:00
|
|
|
/// #[catch(400)]
|
2016-11-05 18:35:21 +00:00
|
|
|
/// fn not_found(req: &Request) -> String {
|
|
|
|
/// format!("I couldn't find '{}'. Try something else?", req.uri())
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// # if false { // We don't actually want to launch the server in an example.
|
2017-09-23 02:04:14 +00:00
|
|
|
/// rocket::ignite().catch(catchers![internal_error, not_found])
|
2017-03-16 05:10:09 +00:00
|
|
|
/// # .launch();
|
2016-11-05 18:35:21 +00:00
|
|
|
/// # }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2017-05-19 10:29:08 +00:00
|
|
|
#[inline]
|
2016-10-04 02:48:33 +00:00
|
|
|
pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
|
2017-06-02 04:44:31 +00:00
|
|
|
info!("👾 {}:", Paint::purple("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!)";
|
2017-06-02 04:44:31 +00:00
|
|
|
info_!("{} {}", c, Paint::yellow(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
|
|
|
|
}
|
|
|
|
|
2017-01-21 03:31:46 +00:00
|
|
|
/// Add `state` to the state managed by this instance of Rocket.
|
|
|
|
///
|
2017-02-03 02:16:57 +00:00
|
|
|
/// This method can be called any number of times as long as each call
|
2017-02-06 18:40:43 +00:00
|
|
|
/// refers to a different `T`.
|
2017-02-03 02:16:57 +00:00
|
|
|
///
|
2017-01-21 03:31:46 +00:00
|
|
|
/// Managed state can be retrieved by any request handler via the
|
|
|
|
/// [State](/rocket/struct.State.html) request guard. In particular, if a
|
|
|
|
/// value of type `T` is managed by Rocket, adding `State<T>` to the list of
|
|
|
|
/// arguments in a request handler instructs Rocket to retrieve the managed
|
|
|
|
/// value.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if state of type `T` is already being managed.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2017-08-29 03:14:59 +00:00
|
|
|
/// # #![feature(plugin, decl_macro)]
|
2017-01-21 03:31:46 +00:00
|
|
|
/// # #![plugin(rocket_codegen)]
|
|
|
|
/// # extern crate rocket;
|
|
|
|
/// use rocket::State;
|
|
|
|
///
|
|
|
|
/// struct MyValue(usize);
|
|
|
|
///
|
|
|
|
/// #[get("/")]
|
|
|
|
/// fn index(state: State<MyValue>) -> String {
|
|
|
|
/// format!("The stateful value is: {}", state.0)
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// # if false { // We don't actually want to launch the server in an example.
|
|
|
|
/// rocket::ignite()
|
2017-02-02 10:16:21 +00:00
|
|
|
/// .mount("/", routes![index])
|
2017-01-21 03:31:46 +00:00
|
|
|
/// .manage(MyValue(10))
|
2017-03-16 05:10:09 +00:00
|
|
|
/// .launch();
|
2017-01-21 03:31:46 +00:00
|
|
|
/// # }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2017-05-19 10:29:08 +00:00
|
|
|
#[inline]
|
2017-01-21 03:31:46 +00:00
|
|
|
pub fn manage<T: Send + Sync + 'static>(self, state: T) -> Self {
|
|
|
|
if !self.state.set::<T>(state) {
|
|
|
|
error!("State for this type is already being managed!");
|
|
|
|
panic!("Aborting due to duplicately managed state.");
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2017-12-27 23:45:30 +00:00
|
|
|
/// Returns `Some` of the managed state value for the type `T` if it is
|
|
|
|
/// being managed by this instance of Rocket. Otherwise, returns `None`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// #[derive(PartialEq, Debug)]
|
|
|
|
/// struct MyState(&'static str);
|
|
|
|
///
|
|
|
|
/// let rocket = rocket::ignite().manage(MyState("hello!"));
|
|
|
|
/// assert_eq!(rocket.state::<MyState>(), Some(&MyState("hello!")));
|
|
|
|
///
|
|
|
|
/// let client = rocket::local::Client::new(rocket).expect("valid rocket");
|
|
|
|
/// assert_eq!(client.rocket().state::<MyState>(), Some(&MyState("hello!")));
|
|
|
|
/// ```
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn state<T: Send + Sync + 'static>(&self) -> Option<&T> {
|
|
|
|
self.state.try_get()
|
|
|
|
}
|
|
|
|
|
2017-05-15 04:46:01 +00:00
|
|
|
/// Attaches a fairing to this instance of Rocket.
|
2017-04-20 20:43:01 +00:00
|
|
|
///
|
2017-05-15 04:46:01 +00:00
|
|
|
/// # Example
|
2017-04-20 20:43:01 +00:00
|
|
|
///
|
|
|
|
/// ```rust
|
2017-08-29 03:14:59 +00:00
|
|
|
/// # #![feature(plugin, decl_macro)]
|
2017-04-20 20:43:01 +00:00
|
|
|
/// # #![plugin(rocket_codegen)]
|
|
|
|
/// # extern crate rocket;
|
2017-05-15 04:46:01 +00:00
|
|
|
/// use rocket::Rocket;
|
|
|
|
/// use rocket::fairing::AdHoc;
|
2017-04-20 20:43:01 +00:00
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// # if false { // We don't actually want to launch the server in an example.
|
|
|
|
/// rocket::ignite()
|
2017-06-12 22:08:34 +00:00
|
|
|
/// .attach(AdHoc::on_launch(|_| println!("Rocket is launching!")))
|
2017-04-20 20:43:01 +00:00
|
|
|
/// .launch();
|
|
|
|
/// # }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
#[inline]
|
2017-05-15 04:46:01 +00:00
|
|
|
pub fn attach<F: Fairing>(mut self, fairing: F) -> Self {
|
2017-05-17 08:39:36 +00:00
|
|
|
// Attach the fairings, which requires us to move `self`.
|
|
|
|
let mut fairings = mem::replace(&mut self.fairings, Fairings::new());
|
|
|
|
self = fairings.attach(Box::new(fairing), self);
|
|
|
|
|
2018-01-12 16:34:53 +00:00
|
|
|
// Make sure we keep all fairings around: the old and newly added ones!
|
|
|
|
fairings.append(self.fairings);
|
2017-05-17 08:39:36 +00:00
|
|
|
self.fairings = fairings;
|
2017-04-20 20:43:01 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2017-05-17 08:39:36 +00:00
|
|
|
pub(crate) fn prelaunch_check(&self) -> Option<LaunchError> {
|
2017-08-19 01:37:25 +00:00
|
|
|
let collisions = self.router.collisions();
|
|
|
|
if !collisions.is_empty() {
|
|
|
|
let owned = collisions.iter().map(|&(a, b)| (a.clone(), b.clone()));
|
|
|
|
Some(LaunchError::from(LaunchErrorKind::Collision(owned.collect())))
|
2017-05-17 08:39:36 +00:00
|
|
|
} else if self.fairings.had_failure() {
|
|
|
|
Some(LaunchError::from(LaunchErrorKind::FailedFairing))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-04 13:35:04 +00:00
|
|
|
/// Starts the application server and begins listening for and dispatching
|
2017-03-16 07:51:28 +00:00
|
|
|
/// requests to mounted routes and catchers. Unless there is an error, this
|
|
|
|
/// function does not return and blocks until program termination.
|
2016-11-04 13:35:04 +00:00
|
|
|
///
|
2017-03-16 05:10:09 +00:00
|
|
|
/// # Error
|
2016-11-04 13:35:04 +00:00
|
|
|
///
|
2017-06-30 20:50:36 +00:00
|
|
|
/// If there is a problem starting the application, a [`LaunchError`] is
|
|
|
|
/// returned. Note that a value of type `LaunchError` panics if dropped
|
|
|
|
/// without first being inspected. See the [`LaunchError`] documentation for
|
|
|
|
/// more information.
|
|
|
|
///
|
|
|
|
/// [`LaunchError`]: /rocket/error/struct.LaunchError.html
|
2016-11-04 13:35:04 +00:00
|
|
|
///
|
2017-06-12 22:08:34 +00:00
|
|
|
/// # Example
|
2016-11-04 13:35:04 +00:00
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # if false {
|
2017-03-16 05:10:09 +00:00
|
|
|
/// rocket::ignite().launch();
|
2016-11-04 13:35:04 +00:00
|
|
|
/// # }
|
|
|
|
/// ```
|
2017-05-19 10:29:08 +00:00
|
|
|
pub fn launch(mut self) -> LaunchError {
|
2017-05-17 08:39:36 +00:00
|
|
|
if let Some(error) = self.prelaunch_check() {
|
|
|
|
return error;
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 20:43:01 +00:00
|
|
|
self.fairings.pretty_print_counts();
|
|
|
|
|
2017-01-12 02:31:37 +00:00
|
|
|
let full_addr = format!("{}:{}", self.config.address, self.config.port);
|
2017-04-13 07:18:31 +00:00
|
|
|
serve!(self, &full_addr, |server, proto| {
|
2017-04-19 09:23:06 +00:00
|
|
|
let mut server = match server {
|
2017-04-13 07:18:31 +00:00
|
|
|
Ok(server) => server,
|
2017-09-05 03:11:10 +00:00
|
|
|
Err(e) => return LaunchError::from(e),
|
2017-04-13 07:18:31 +00:00
|
|
|
};
|
2016-10-03 10:39:56 +00:00
|
|
|
|
2017-05-19 10:29:08 +00:00
|
|
|
// Determine the address and port we actually binded to.
|
|
|
|
match server.local_addr() {
|
|
|
|
Ok(server_addr) => self.config.port = server_addr.port(),
|
2017-09-05 03:11:10 +00:00
|
|
|
Err(e) => return LaunchError::from(e),
|
2017-05-19 10:29:08 +00:00
|
|
|
}
|
2017-04-19 09:23:06 +00:00
|
|
|
|
2017-05-17 08:39:36 +00:00
|
|
|
// Run the launch fairings.
|
|
|
|
self.fairings.handle_launch(&self);
|
2017-04-20 20:43:01 +00:00
|
|
|
|
2017-05-19 10:29:08 +00:00
|
|
|
let full_addr = format!("{}:{}", self.config.address, self.config.port);
|
2017-09-05 03:22:10 +00:00
|
|
|
launch_info!("{}{} {}{}",
|
|
|
|
Paint::masked("🚀 "),
|
2017-09-05 03:11:10 +00:00
|
|
|
Paint::white("Rocket has launched from"),
|
|
|
|
Paint::white(proto).bold(),
|
|
|
|
Paint::white(&full_addr).bold());
|
2016-10-03 10:39:56 +00:00
|
|
|
|
2017-04-13 07:18:31 +00:00
|
|
|
let threads = self.config.workers as usize;
|
|
|
|
if let Err(e) = server.handle_threads(self, threads) {
|
|
|
|
return LaunchError::from(e);
|
|
|
|
}
|
2017-03-16 05:10:09 +00:00
|
|
|
|
2017-04-13 07:18:31 +00:00
|
|
|
unreachable!("the call to `handle_threads` should block on success")
|
|
|
|
})
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|
2017-04-05 08:19:33 +00:00
|
|
|
|
2017-06-12 22:08:34 +00:00
|
|
|
/// Returns an iterator over all of the routes mounted on this instance of
|
|
|
|
/// Rocket.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2017-08-29 03:14:59 +00:00
|
|
|
/// # #![feature(plugin, decl_macro)]
|
2017-06-12 22:08:34 +00:00
|
|
|
/// # #![plugin(rocket_codegen)]
|
|
|
|
/// # extern crate rocket;
|
|
|
|
/// use rocket::Rocket;
|
|
|
|
/// use rocket::fairing::AdHoc;
|
|
|
|
///
|
|
|
|
/// #[get("/hello")]
|
|
|
|
/// fn hello() -> &'static str {
|
|
|
|
/// "Hello, world!"
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// let rocket = rocket::ignite()
|
|
|
|
/// .mount("/", routes![hello])
|
|
|
|
/// .mount("/hi", routes![hello]);
|
|
|
|
///
|
|
|
|
/// for route in rocket.routes() {
|
|
|
|
/// match route.base() {
|
|
|
|
/// "/" => assert_eq!(route.uri.path(), "/hello"),
|
|
|
|
/// "/hi" => assert_eq!(route.uri.path(), "/hi/hello"),
|
|
|
|
/// _ => unreachable!("only /hello, /hi/hello are expected")
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// assert_eq!(rocket.routes().count(), 2);
|
|
|
|
/// }
|
|
|
|
/// ```
|
2017-04-05 08:19:33 +00:00
|
|
|
#[inline(always)]
|
2017-09-05 03:11:10 +00:00
|
|
|
pub fn routes<'a>(&'a self) -> impl Iterator<Item = &'a Route> + 'a {
|
2017-04-05 08:19:33 +00:00
|
|
|
self.router.routes()
|
|
|
|
}
|
2017-04-20 20:43:01 +00:00
|
|
|
|
2017-06-12 22:08:34 +00:00
|
|
|
/// Returns the active configuration.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2017-08-29 03:14:59 +00:00
|
|
|
/// # #![feature(plugin, decl_macro)]
|
2017-06-12 22:08:34 +00:00
|
|
|
/// # #![plugin(rocket_codegen)]
|
|
|
|
/// # extern crate rocket;
|
|
|
|
/// use rocket::Rocket;
|
|
|
|
/// use rocket::fairing::AdHoc;
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// # if false { // We don't actually want to launch the server in an example.
|
|
|
|
/// rocket::ignite()
|
|
|
|
/// .attach(AdHoc::on_launch(|rocket| {
|
|
|
|
/// println!("Rocket launch config: {:?}", rocket.config());
|
|
|
|
/// }))
|
|
|
|
/// .launch();
|
|
|
|
/// # }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2017-04-20 20:43:01 +00:00
|
|
|
#[inline(always)]
|
|
|
|
pub fn config(&self) -> &Config {
|
2017-05-19 10:29:08 +00:00
|
|
|
&self.config
|
2017-04-20 20:43:01 +00:00
|
|
|
}
|
2016-04-01 23:54:53 +00:00
|
|
|
}
|