mirror of https://github.com/rwf2/Rocket.git
Remove config global state. Use Responder::respond_to.
This commit includes two major changes to core: 1. Configuration state is no longer global. The `config::active()` function has been removed. The active configuration can be retrieved via the `config` method on a `Rocket` instance. 2. The `Responder` trait has changed. `Responder::respond(self)` has been removed in favor of `Responder::respond_to(self, &Request)`. This allows responders to dynamically adjust their response based on the incoming request. Additionally, it includes the following changes to core and codegen: * The `Request::guard` method was added to allow for simple retrivial of request guards. * The `Request::limits` method was added to retrieve configured limits. * The `File` `Responder` implementation now uses a fixed size body instead of a chunked body. * The `Outcome::of<R: Responder>(R)` method was removed while `Outcome::from<R: Responder(&Request, R)` was added. * The unmounted and unmanaged limits are more cautious: they will only emit warnings when the `Rocket` receiver is known. This commit includes one major change to contrib: 1. To use contrib's templating, the fairing returned by `Template::fairing()` must be attached to the running Rocket instance. Additionally, the `Display` implementation of `Template` was removed. To directly render a template to a `String`, the new `Template::show` method can be used.
This commit is contained in:
parent
28a1ef0916
commit
9b955747e4
|
@ -8,8 +8,8 @@ use syntax::ext::base::{Annotatable, ExtCtxt};
|
|||
use syntax::tokenstream::TokenTree;
|
||||
use syntax::parse::token;
|
||||
|
||||
const ERR_PARAM: &'static str = "_error";
|
||||
const REQ_PARAM: &'static str = "_request";
|
||||
const ERR_PARAM: &'static str = "__err";
|
||||
const REQ_PARAM: &'static str = "__req";
|
||||
|
||||
trait ErrorGenerateExt {
|
||||
fn generate_fn_arguments(&self, &ExtCtxt, Ident, Ident) -> Vec<TokenTree>;
|
||||
|
@ -71,7 +71,8 @@ pub fn error_decorator(ecx: &mut ExtCtxt,
|
|||
$req_ident: &'_b ::rocket::Request)
|
||||
-> ::rocket::response::Result<'_b> {
|
||||
let user_response = $user_fn_name($fn_arguments);
|
||||
let response = ::rocket::response::Responder::respond(user_response)?;
|
||||
let response = ::rocket::response::Responder::respond_to(user_response,
|
||||
$req_ident)?;
|
||||
let status = ::rocket::http::Status::raw($code);
|
||||
::rocket::response::Response::build().status(status).merge(response).ok()
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ impl RouteGenerateExt for RouteParams {
|
|||
let mut items = ::rocket::request::FormItems::from($form_string);
|
||||
let obj = match ::rocket::request::FromForm::from_form_items(items.by_ref()) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return ::rocket::Outcome::Forward(_data)
|
||||
Err(_) => return ::rocket::Outcome::Forward(__data)
|
||||
};
|
||||
|
||||
if !items.exhaust() {
|
||||
|
@ -106,7 +106,7 @@ impl RouteGenerateExt for RouteParams {
|
|||
let ty = strip_ty_lifetimes(arg.ty.clone());
|
||||
Some(quote_stmt!(ecx,
|
||||
let $name: $ty =
|
||||
match ::rocket::data::FromData::from_data(_req, _data) {
|
||||
match ::rocket::data::FromData::from_data(__req, __data) {
|
||||
::rocket::Outcome::Success(d) => d,
|
||||
::rocket::Outcome::Forward(d) =>
|
||||
return ::rocket::Outcome::Forward(d),
|
||||
|
@ -120,9 +120,9 @@ impl RouteGenerateExt for RouteParams {
|
|||
fn generate_query_statement(&self, ecx: &ExtCtxt) -> Option<Stmt> {
|
||||
let param = self.query_param.as_ref();
|
||||
let expr = quote_expr!(ecx,
|
||||
match _req.uri().query() {
|
||||
match __req.uri().query() {
|
||||
Some(query) => query,
|
||||
None => return ::rocket::Outcome::Forward(_data)
|
||||
None => return ::rocket::Outcome::Forward(__data)
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -149,13 +149,13 @@ impl RouteGenerateExt for RouteParams {
|
|||
// Note: the `None` case shouldn't happen if a route is matched.
|
||||
let ident = param.ident().prepend(PARAM_PREFIX);
|
||||
let expr = match param {
|
||||
Param::Single(_) => quote_expr!(ecx, match _req.get_param_str($i) {
|
||||
Param::Single(_) => quote_expr!(ecx, match __req.get_param_str($i) {
|
||||
Some(s) => <$ty as ::rocket::request::FromParam>::from_param(s),
|
||||
None => return ::rocket::Outcome::Forward(_data)
|
||||
None => return ::rocket::Outcome::Forward(__data)
|
||||
}),
|
||||
Param::Many(_) => quote_expr!(ecx, match _req.get_raw_segments($i) {
|
||||
Param::Many(_) => quote_expr!(ecx, match __req.get_raw_segments($i) {
|
||||
Some(s) => <$ty as ::rocket::request::FromSegments>::from_segments(s),
|
||||
None => return ::rocket::Outcome::Forward(_data)
|
||||
None => return ::rocket::Outcome::Forward(__data)
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -166,7 +166,7 @@ impl RouteGenerateExt for RouteParams {
|
|||
Err(e) => {
|
||||
println!(" => Failed to parse '{}': {:?}",
|
||||
stringify!($original_ident), e);
|
||||
return ::rocket::Outcome::Forward(_data)
|
||||
return ::rocket::Outcome::Forward(__data)
|
||||
}
|
||||
};
|
||||
).expect("declared param parsing statement"));
|
||||
|
@ -195,10 +195,10 @@ impl RouteGenerateExt for RouteParams {
|
|||
fn_param_statements.push(quote_stmt!(ecx,
|
||||
#[allow(non_snake_case)]
|
||||
let $ident: $ty = match
|
||||
::rocket::request::FromRequest::from_request(_req) {
|
||||
::rocket::request::FromRequest::from_request(__req) {
|
||||
::rocket::outcome::Outcome::Success(v) => v,
|
||||
::rocket::outcome::Outcome::Forward(_) =>
|
||||
return ::rocket::Outcome::forward(_data),
|
||||
return ::rocket::Outcome::forward(__data),
|
||||
::rocket::outcome::Outcome::Failure((code, _)) => {
|
||||
return ::rocket::Outcome::Failure(code)
|
||||
},
|
||||
|
@ -254,13 +254,13 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
|
|||
// Allow the `unreachable_code` lint for those FromParam impls that have
|
||||
// an `Error` associated type of !.
|
||||
#[allow(unreachable_code)]
|
||||
fn $route_fn_name<'_b>(_req: &'_b ::rocket::Request, _data: ::rocket::Data)
|
||||
fn $route_fn_name<'_b>(__req: &'_b ::rocket::Request, __data: ::rocket::Data)
|
||||
-> ::rocket::handler::Outcome<'_b> {
|
||||
$param_statements
|
||||
$query_statement
|
||||
$data_statement
|
||||
let responder = $user_fn_name($fn_arguments);
|
||||
::rocket::handler::Outcome::of(responder)
|
||||
::rocket::handler::Outcome::from(__req, responder)
|
||||
}
|
||||
).unwrap());
|
||||
|
||||
|
|
|
@ -174,11 +174,11 @@ pub fn msg_and_help<'a, T: LintContext<'a>>(cx: &T,
|
|||
note: &str,
|
||||
help_sp: Option<Span>,
|
||||
help: &str) {
|
||||
let mut b = cx.struct_span_lint(lint, msg_sp, msg);
|
||||
b.note(note);
|
||||
// Be conservative. If we don't know the receiver, don't emit the warning.
|
||||
if let Some(span) = help_sp {
|
||||
b.span_help(span, help);
|
||||
cx.struct_span_lint(lint, msg_sp, msg)
|
||||
.note(note)
|
||||
.span_help(span, help)
|
||||
.emit()
|
||||
}
|
||||
|
||||
b.emit();
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ tera_templates = ["tera", "templates"]
|
|||
handlebars_templates = ["handlebars", "templates"]
|
||||
|
||||
# Internal use only.
|
||||
templates = ["serde", "serde_json", "lazy_static_macro", "glob"]
|
||||
lazy_static_macro = ["lazy_static"]
|
||||
templates = ["serde", "serde_json", "glob"]
|
||||
|
||||
[dependencies]
|
||||
rocket = { version = "0.2.6", path = "../lib/" }
|
||||
|
@ -36,5 +35,4 @@ rmp-serde = { version = "^0.13", optional = true }
|
|||
# Templating dependencies only.
|
||||
handlebars = { version = "^0.26.1", optional = true }
|
||||
glob = { version = "^0.2", optional = true }
|
||||
lazy_static = { version = "^0.2", optional = true }
|
||||
tera = { version = "^0.10", optional = true }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
use std::io::Read;
|
||||
|
||||
use rocket::config;
|
||||
use rocket::outcome::{Outcome, IntoOutcome};
|
||||
use rocket::request::Request;
|
||||
use rocket::data::{self, Data, FromData};
|
||||
|
@ -80,6 +79,7 @@ impl<T> JSON<T> {
|
|||
/// let my_json = JSON(string);
|
||||
/// assert_eq!(my_json.into_inner(), "Hello".to_string());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
|
@ -97,10 +97,7 @@ impl<T: DeserializeOwned> FromData for JSON<T> {
|
|||
return Outcome::Forward(data);
|
||||
}
|
||||
|
||||
let size_limit = config::active()
|
||||
.and_then(|c| c.limits.get("json"))
|
||||
.unwrap_or(LIMIT);
|
||||
|
||||
let size_limit = request.limits().get("json").unwrap_or(LIMIT);
|
||||
serde_json::from_reader(data.open().take(size_limit))
|
||||
.map(|val| JSON(val))
|
||||
.map_err(|e| { error_!("Couldn't parse JSON body: {:?}", e); e })
|
||||
|
@ -112,9 +109,9 @@ impl<T: DeserializeOwned> FromData for JSON<T> {
|
|||
/// JSON and a fixed-size body with the serialized value. If serialization
|
||||
/// fails, an `Err` of `Status::InternalServerError` is returned.
|
||||
impl<T: Serialize> Responder<'static> for JSON<T> {
|
||||
fn respond(self) -> response::Result<'static> {
|
||||
fn respond_to(self, req: &Request) -> response::Result<'static> {
|
||||
serde_json::to_string(&self.0).map(|string| {
|
||||
content::JSON(string).respond().unwrap()
|
||||
content::JSON(string).respond_to(req).unwrap()
|
||||
}).map_err(|e| {
|
||||
error_!("JSON failed to serialize: {:?}", e);
|
||||
Status::InternalServerError
|
||||
|
@ -125,12 +122,14 @@ impl<T: Serialize> Responder<'static> for JSON<T> {
|
|||
impl<T> Deref for JSON<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref<'a>(&'a self) -> &'a T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for JSON<T> {
|
||||
#[inline(always)]
|
||||
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
|
||||
&mut self.0
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#![feature(drop_types_in_const, macro_reexport)]
|
||||
#![cfg_attr(feature = "templates", feature(conservative_impl_trait))]
|
||||
#![cfg_attr(feature = "templates", feature(associated_consts))]
|
||||
#![cfg_attr(feature = "templates", feature(struct_field_attributes))]
|
||||
|
||||
//! This crate contains officially sanctioned contributor libraries that provide
|
||||
//! functionality commonly used by Rocket applications.
|
||||
|
@ -37,10 +40,6 @@
|
|||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
#[cfg_attr(feature = "lazy_static_macro", macro_use)]
|
||||
#[cfg(feature = "lazy_static_macro")]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
extern crate serde;
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ extern crate rmp_serde;
|
|||
use std::ops::{Deref, DerefMut};
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use rocket::config;
|
||||
use rocket::outcome::{Outcome, IntoOutcome};
|
||||
use rocket::request::Request;
|
||||
use rocket::data::{self, Data, FromData};
|
||||
|
@ -111,11 +110,8 @@ impl<T: DeserializeOwned> FromData for MsgPack<T> {
|
|||
return Outcome::Forward(data);
|
||||
}
|
||||
|
||||
let size_limit = config::active()
|
||||
.and_then(|c| c.limits.get("msgpack"))
|
||||
.unwrap_or(LIMIT);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let size_limit = request.limits().get("msgpack").unwrap_or(LIMIT);
|
||||
if let Err(e) = data.open().take(size_limit).read_to_end(&mut buf) {
|
||||
let e = MsgPackError::InvalidDataRead(e);
|
||||
error_!("Couldn't read request data: {:?}", e);
|
||||
|
@ -132,7 +128,7 @@ impl<T: DeserializeOwned> FromData for MsgPack<T> {
|
|||
/// Content-Type `MsgPack` and a fixed-size body with the serialization. If
|
||||
/// serialization fails, an `Err` of `Status::InternalServerError` is returned.
|
||||
impl<T: Serialize> Responder<'static> for MsgPack<T> {
|
||||
fn respond(self) -> response::Result<'static> {
|
||||
fn respond_to(self, _: &Request) -> response::Result<'static> {
|
||||
rmp_serde::to_vec(&self.0).map_err(|e| {
|
||||
error_!("MsgPack failed to serialize: {:?}", e);
|
||||
Status::InternalServerError
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{Engines, TemplateInfo};
|
||||
use super::glob;
|
||||
|
||||
use rocket::http::ContentType;
|
||||
|
||||
pub struct Context {
|
||||
/// The root of the template directory.
|
||||
pub root: PathBuf,
|
||||
/// Mapping from template name to its information.
|
||||
pub templates: HashMap<String, TemplateInfo>,
|
||||
/// Mapping from template name to its information.
|
||||
pub engines: Engines
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn initialize(root: PathBuf) -> Option<Context> {
|
||||
let mut templates: HashMap<String, TemplateInfo> = HashMap::new();
|
||||
for ext in Engines::ENABLED_EXTENSIONS {
|
||||
let mut glob_path = root.join("**").join("*");
|
||||
glob_path.set_extension(ext);
|
||||
let glob_path = glob_path.to_str().expect("valid glob path string");
|
||||
|
||||
for path in glob(glob_path).unwrap().filter_map(Result::ok) {
|
||||
let (name, data_type_str) = split_path(&root, &path);
|
||||
if let Some(info) = templates.get(&*name) {
|
||||
warn_!("Template name '{}' does not have a unique path.", name);
|
||||
info_!("Existing path: {:?}", info.path);
|
||||
info_!("Additional path: {:?}", path);
|
||||
warn_!("Using existing path for template '{}'.", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
let data_type = data_type_str.as_ref()
|
||||
.map(|ext| ContentType::from_extension(ext))
|
||||
.unwrap_or(ContentType::HTML);
|
||||
|
||||
templates.insert(name, TemplateInfo {
|
||||
path: path.to_path_buf(),
|
||||
extension: ext.to_string(),
|
||||
data_type: data_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Engines::init(&templates).map(|engines| {
|
||||
Context { root, templates, engines }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the file path's extension or does nothing if there is none.
|
||||
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
|
||||
let path = path.as_ref();
|
||||
let stem = match path.file_stem() {
|
||||
Some(stem) => stem,
|
||||
None => return path.to_path_buf()
|
||||
};
|
||||
|
||||
match path.parent() {
|
||||
Some(parent) => parent.join(stem),
|
||||
None => PathBuf::from(stem)
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits a path into a name that may be used to identify the template, and the
|
||||
/// template's data type, if any.
|
||||
fn split_path(root: &Path, path: &Path) -> (String, Option<String>) {
|
||||
let rel_path = path.strip_prefix(root).unwrap().to_path_buf();
|
||||
let path_no_ext = remove_extension(&rel_path);
|
||||
let data_type = path_no_ext.extension();
|
||||
let mut name = remove_extension(&path_no_ext).to_string_lossy().into_owned();
|
||||
|
||||
// Ensure template name consistency on Windows systems
|
||||
if cfg!(windows) {
|
||||
name = name.replace("\\", "/");
|
||||
}
|
||||
|
||||
(name, data_type.map(|d| d.to_string_lossy().into_owned()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn template_path_index_html() {
|
||||
for root in &["/", "/a/b/c/", "/a/b/c/d/", "/a/"] {
|
||||
for filename in &["index.html.hbs", "index.html.tera"] {
|
||||
let path = Path::new(root).join(filename);
|
||||
let (name, data_type) = split_path(Path::new(root), &path);
|
||||
|
||||
assert_eq!(name, "index");
|
||||
assert_eq!(data_type, Some("html".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_path_subdir_index_html() {
|
||||
for root in &["/", "/a/b/c/", "/a/b/c/d/", "/a/"] {
|
||||
for sub in &["a/", "a/b/", "a/b/c/", "a/b/c/d/"] {
|
||||
for filename in &["index.html.hbs", "index.html.tera"] {
|
||||
let path = Path::new(root).join(sub).join(filename);
|
||||
let (name, data_type) = split_path(Path::new(root), &path);
|
||||
|
||||
let expected_name = format!("{}index", sub);
|
||||
assert_eq!(name, expected_name.as_str());
|
||||
assert_eq!(data_type, Some("html".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_path_doc_examples() {
|
||||
fn name_for(path: &str) -> String {
|
||||
split_path(Path::new("templates/"), &Path::new("templates/").join(path)).0
|
||||
}
|
||||
|
||||
assert_eq!(name_for("index.html.hbs"), "index");
|
||||
assert_eq!(name_for("index.tera"), "index");
|
||||
assert_eq!(name_for("index.hbs"), "index");
|
||||
assert_eq!(name_for("dir/index.hbs"), "dir/index");
|
||||
assert_eq!(name_for("dir/index.html.tera"), "dir/index");
|
||||
assert_eq!(name_for("index.template.html.hbs"), "index.template");
|
||||
assert_eq!(name_for("subdir/index.template.html.hbs"), "subdir/index.template");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use super::serde::Serialize;
|
||||
use super::TemplateInfo;
|
||||
|
||||
#[cfg(feature = "tera_templates")] use super::tera_templates::Tera;
|
||||
#[cfg(feature = "handlebars_templates")] use super::handlebars_templates::Handlebars;
|
||||
|
||||
pub trait Engine: Send + Sync + 'static {
|
||||
const EXT: &'static str;
|
||||
|
||||
fn init(templates: &[(&str, &TemplateInfo)]) -> Option<Self> where Self: Sized;
|
||||
fn render<C: Serialize>(&self, name: &str, context: C) -> Option<String>;
|
||||
}
|
||||
|
||||
pub struct Engines {
|
||||
#[cfg(feature = "tera_templates")]
|
||||
tera: Tera,
|
||||
#[cfg(feature = "handlebars_templates")]
|
||||
handlebars: Handlebars,
|
||||
}
|
||||
|
||||
impl Engines {
|
||||
pub const ENABLED_EXTENSIONS: &'static [&'static str] = &[
|
||||
#[cfg(feature = "tera_templates")] Tera::EXT,
|
||||
#[cfg(feature = "handlebars_templates")] Handlebars::EXT,
|
||||
];
|
||||
|
||||
pub fn init(templates: &HashMap<String, TemplateInfo>) -> Option<Engines> {
|
||||
fn inner<E: Engine>(templates: &HashMap<String, TemplateInfo>) -> Option<E> {
|
||||
let named_templates = templates.iter()
|
||||
.filter(|&(_, i)| i.extension == E::EXT)
|
||||
.map(|(k, i)| (k.as_str(), i))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
E::init(&*named_templates)
|
||||
}
|
||||
|
||||
Some(Engines {
|
||||
#[cfg(feature = "tera_templates")]
|
||||
tera: match inner::<Tera>(templates) {
|
||||
Some(tera) => tera,
|
||||
None => return None
|
||||
},
|
||||
#[cfg(feature = "handlebars_templates")]
|
||||
handlebars: match inner::<Handlebars>(templates) {
|
||||
Some(hb) => hb,
|
||||
None => return None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render<C>(&self, name: &str, info: &TemplateInfo, c: C) -> Option<String>
|
||||
where C: Serialize
|
||||
{
|
||||
#[cfg(feature = "tera_templates")]
|
||||
{
|
||||
if info.extension == Tera::EXT {
|
||||
return Engine::render(&self.tera, name, c);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "handlebars_templates")]
|
||||
{
|
||||
if info.extension == Handlebars::EXT {
|
||||
return Engine::render(&self.handlebars, name, c);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
|
@ -1,57 +1,40 @@
|
|||
extern crate handlebars;
|
||||
|
||||
use super::serde::Serialize;
|
||||
use super::TemplateInfo;
|
||||
use super::{Engine, TemplateInfo};
|
||||
|
||||
use self::handlebars::Handlebars;
|
||||
pub use self::handlebars::Handlebars;
|
||||
|
||||
static mut HANDLEBARS: Option<Handlebars> = None;
|
||||
impl Engine for Handlebars {
|
||||
const EXT: &'static str = "hbs";
|
||||
|
||||
pub const EXT: &'static str = "hbs";
|
||||
|
||||
// This function must be called a SINGLE TIME from A SINGLE THREAD for safety to
|
||||
// hold here and in `render`.
|
||||
pub unsafe fn register(templates: &[(&str, &TemplateInfo)]) -> bool {
|
||||
if HANDLEBARS.is_some() {
|
||||
error_!("Internal error: reregistering handlebars!");
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut hb = Handlebars::new();
|
||||
let mut success = true;
|
||||
for &(name, info) in templates {
|
||||
let path = &info.full_path;
|
||||
if let Err(e) = hb.register_template_file(name, path) {
|
||||
error_!("Handlebars template '{}' failed registry: {:?}", name, e);
|
||||
success = false;
|
||||
fn init(templates: &[(&str, &TemplateInfo)]) -> Option<Handlebars> {
|
||||
let mut hb = Handlebars::new();
|
||||
for &(name, info) in templates {
|
||||
let path = &info.path;
|
||||
if let Err(e) = hb.register_template_file(name, path) {
|
||||
error!("Error in Handlebars template '{}'.", name);
|
||||
info_!("{}", e);
|
||||
info_!("Template path: '{}'.", path.to_string_lossy());
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(hb)
|
||||
}
|
||||
|
||||
HANDLEBARS = Some(hb);
|
||||
success
|
||||
}
|
||||
|
||||
pub fn render<T>(name: &str, _info: &TemplateInfo, context: &T) -> Option<String>
|
||||
where T: Serialize
|
||||
{
|
||||
let hb = match unsafe { HANDLEBARS.as_ref() } {
|
||||
Some(hb) => hb,
|
||||
None => {
|
||||
error_!("Internal error: `render` called before handlebars init.");
|
||||
fn render<C: Serialize>(&self, name: &str, context: C) -> Option<String> {
|
||||
if self.get_template(name).is_none() {
|
||||
error_!("Handlebars template '{}' does not exist.", name);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if hb.get_template(name).is_none() {
|
||||
error_!("Handlebars template '{}' does not exist.", name);
|
||||
return None;
|
||||
}
|
||||
|
||||
match hb.render(name, context) {
|
||||
Ok(string) => Some(string),
|
||||
Err(e) => {
|
||||
error_!("Error rendering Handlebars template '{}': {}", name, e);
|
||||
None
|
||||
match self.render(name, &context) {
|
||||
Ok(string) => Some(string),
|
||||
Err(e) => {
|
||||
error_!("Error rendering Handlebars template '{}': {}", name, e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
/// Returns a hashset with the extensions of all of the enabled template
|
||||
/// engines from the set of template engined passed in.
|
||||
macro_rules! engine_set {
|
||||
($($feature:expr => $engine:ident),+,) => ({
|
||||
type RegisterFn = for<'a, 'b> unsafe fn(&'a [(&'b str, &TemplateInfo)]) -> bool;
|
||||
|
||||
let mut set = Vec::new();
|
||||
$(
|
||||
#[cfg(feature = $feature)]
|
||||
fn $engine(set: &mut Vec<(&'static str, RegisterFn)>) {
|
||||
set.push(($engine::EXT, $engine::register));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = $feature))]
|
||||
fn $engine(_: &mut Vec<(&'static str, RegisterFn)>) { }
|
||||
|
||||
$engine(&mut set);
|
||||
)+
|
||||
|
||||
set
|
||||
});
|
||||
}
|
||||
|
||||
/// Renders the template named `name` with the given template info `info` and
|
||||
/// context `ctxt` using one of the templates in the template set passed in. It
|
||||
/// does this by checking if the template's extension matches the engine's
|
||||
/// extension, and if so, calls the engine's `render` method. All of this only
|
||||
/// happens for engine's that have been enabled as features by the user.
|
||||
macro_rules! render_set {
|
||||
($name:expr, $info:expr, $ctxt:expr, $($feature:expr => $engine:ident),+,) => ({
|
||||
$(
|
||||
#[cfg(feature = $feature)]
|
||||
fn $engine<T: Serialize>(name: &str, info: &TemplateInfo, c: &T)
|
||||
-> Option<Template> {
|
||||
if info.extension == $engine::EXT {
|
||||
let rendered = $engine::render(name, info, c);
|
||||
return Some(Template(rendered, info.data_type.clone()));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(feature = $feature))]
|
||||
fn $engine<T: Serialize>(_: &str, _: &TemplateInfo, _: &T)
|
||||
-> Option<Template> { None }
|
||||
|
||||
if let Some(template) = $engine($name, &$info, $ctxt) {
|
||||
return template
|
||||
}
|
||||
)+
|
||||
});
|
||||
}
|
||||
|
|
@ -2,25 +2,28 @@ extern crate serde;
|
|||
extern crate serde_json;
|
||||
extern crate glob;
|
||||
|
||||
#[cfg(feature = "tera_templates")]
|
||||
mod tera_templates;
|
||||
|
||||
#[cfg(feature = "handlebars_templates")]
|
||||
mod handlebars_templates;
|
||||
|
||||
#[macro_use] mod macros;
|
||||
#[cfg(feature = "tera_templates")] mod tera_templates;
|
||||
#[cfg(feature = "handlebars_templates")] mod handlebars_templates;
|
||||
mod engine;
|
||||
mod context;
|
||||
|
||||
use self::engine::{Engine, Engines};
|
||||
use self::context::Context;
|
||||
use self::serde::Serialize;
|
||||
use self::serde_json::{Value, to_value};
|
||||
use self::glob::glob;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
use rocket::config::{self, ConfigError};
|
||||
use rocket::State;
|
||||
use rocket::request::Request;
|
||||
use rocket::fairing::{Fairing, AdHoc};
|
||||
use rocket::response::{self, Content, Responder};
|
||||
use rocket::http::{ContentType, Status};
|
||||
|
||||
const DEFAULT_TEMPLATE_DIR: &'static str = "templates";
|
||||
|
||||
/// The Template type implements generic support for template rendering in
|
||||
/// Rocket.
|
||||
///
|
||||
|
@ -28,8 +31,9 @@ use rocket::http::{ContentType, Status};
|
|||
/// the template directory. The template directory is configurable via the
|
||||
/// `template_dir` configuration parameter and defaults to `templates/`. The
|
||||
/// path set in `template_dir` should be relative to the Rocket configuration
|
||||
/// file. See the [configuration chapter](https://rocket.rs/guide/overview/#configuration)
|
||||
/// of the guide for more information on configuration.
|
||||
/// file. See the [configuration
|
||||
/// chapter](https://rocket.rs/guide/overview/#configuration) of the guide for
|
||||
/// more information on configuration.
|
||||
///
|
||||
/// Templates are discovered according to their extension. At present, this
|
||||
/// library supports the following templates and extensions:
|
||||
|
@ -55,6 +59,13 @@ use rocket::http::{ContentType, Status};
|
|||
/// type, and one for the template extension. This means that template
|
||||
/// extensions should look like: `.html.hbs`, `.html.tera`, `.xml.hbs`, etc.
|
||||
///
|
||||
/// Template discovery is actualized by the template fairing, which itself is
|
||||
/// created via the
|
||||
/// [`Template::fairing()`](/rocket_contrib/struct.Template.html#method.fairing)
|
||||
/// method. In order for _any_ templates to be rendered, the template fairing
|
||||
/// must be [attached](/rocket/struct.Rocket.html#method.attach) to the running
|
||||
/// Rocket instance.
|
||||
///
|
||||
/// Templates are rendered with the `render` method. The method takes in the
|
||||
/// name of a template and a context to render the template with. The context
|
||||
/// can be any type that implements `Serialize` from
|
||||
|
@ -74,7 +85,25 @@ use rocket::http::{ContentType, Status};
|
|||
/// features = ["handlebars_templates", "tera_templates"]
|
||||
/// ```
|
||||
///
|
||||
/// The Template type implements Rocket's `Responder` trait, so it can be
|
||||
/// Then, ensure that the template [fairing](/rocket/fairing/) is attached to
|
||||
/// your Rocket application:
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate rocket;
|
||||
/// extern crate rocket_contrib;
|
||||
///
|
||||
/// use rocket_contrib::Template;
|
||||
///
|
||||
/// fn main() {
|
||||
/// rocket::ignite()
|
||||
/// // ...
|
||||
/// .attach(Template::fairing())
|
||||
/// // ...
|
||||
/// # ;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The `Template` type implements Rocket's `Responder` trait, so it can be
|
||||
/// returned from a request handler directly:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
|
@ -84,49 +113,68 @@ use rocket::http::{ContentType, Status};
|
|||
/// Template::render("index", &context)
|
||||
/// }
|
||||
/// ```
|
||||
// Fields are: (optionally rendered template, template extension)
|
||||
#[derive(Debug)]
|
||||
pub struct Template(Option<String>, Option<String>);
|
||||
pub struct Template {
|
||||
name: Cow<'static, str>,
|
||||
value: Option<Value>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TemplateInfo {
|
||||
/// The complete path, including `template_dir`, to this template.
|
||||
full_path: PathBuf,
|
||||
/// The complete path, without `template_dir`, to this template.
|
||||
path: PathBuf,
|
||||
/// The extension for the engine of this template.
|
||||
extension: String,
|
||||
/// The extension before the engine extension in the template, if any.
|
||||
data_type: Option<String>
|
||||
}
|
||||
|
||||
const DEFAULT_TEMPLATE_DIR: &'static str = "templates";
|
||||
|
||||
lazy_static! {
|
||||
static ref TEMPLATES: HashMap<String, TemplateInfo> = discover_templates();
|
||||
static ref TEMPLATE_DIR: PathBuf = {
|
||||
let default_dir_path = config::active().ok_or(ConfigError::NotFound)
|
||||
.map(|config| config.root().join(DEFAULT_TEMPLATE_DIR))
|
||||
.map_err(|_| {
|
||||
warn_!("No configuration is active!");
|
||||
warn_!("Using default template directory: {:?}", DEFAULT_TEMPLATE_DIR);
|
||||
})
|
||||
.unwrap_or(PathBuf::from(DEFAULT_TEMPLATE_DIR));
|
||||
|
||||
config::active().ok_or(ConfigError::NotFound)
|
||||
.and_then(|config| config.get_str("template_dir"))
|
||||
.map(|user_dir| PathBuf::from(user_dir))
|
||||
.map_err(|e| {
|
||||
if !e.is_not_found() {
|
||||
e.pretty_print();
|
||||
warn_!("Using default directory '{:?}'", default_dir_path);
|
||||
}
|
||||
})
|
||||
.unwrap_or(default_dir_path)
|
||||
};
|
||||
data_type: ContentType
|
||||
}
|
||||
|
||||
impl Template {
|
||||
/// Returns a fairing that intializes and maintains templating state.
|
||||
///
|
||||
/// This fairing _must_ be attached to any `Rocket` instance that wishes to
|
||||
/// render templates. Failure to attach this fairing will result in a
|
||||
/// "Uninitialized template context: missing fairing." error message when a
|
||||
/// template is attempted to be rendered.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// To attach this fairing, simple call `attach` on the application's
|
||||
/// `Rocket` instance with `Template::fairing()`:
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate rocket;
|
||||
/// extern crate rocket_contrib;
|
||||
///
|
||||
/// use rocket_contrib::Template;
|
||||
///
|
||||
/// fn main() {
|
||||
/// rocket::ignite()
|
||||
/// // ...
|
||||
/// .attach(Template::fairing())
|
||||
/// // ...
|
||||
/// # ;
|
||||
/// }
|
||||
/// ```
|
||||
pub fn fairing() -> impl Fairing {
|
||||
AdHoc::on_attach(|rocket| {
|
||||
let mut template_root = rocket.config().root().join(DEFAULT_TEMPLATE_DIR);
|
||||
match rocket.config().get_str("template_dir") {
|
||||
Ok(dir) => template_root = rocket.config().root().join(dir),
|
||||
Err(ref e) if !e.is_not_found() => {
|
||||
e.pretty_print();
|
||||
warn_!("Using default directory '{:?}'", template_root);
|
||||
}
|
||||
Err(_) => { }
|
||||
};
|
||||
|
||||
match Context::initialize(template_root) {
|
||||
Some(ctxt) => Ok(rocket.manage(ctxt)),
|
||||
None => Err(rocket)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Render the template named `name` with the context `context`. The
|
||||
/// `context` can be of any type that implements `Serialize`. This is
|
||||
/// typically a `HashMap` or a custom `struct`.
|
||||
|
@ -141,30 +189,73 @@ impl Template {
|
|||
/// let mut context = HashMap::new();
|
||||
///
|
||||
/// # context.insert("test", "test");
|
||||
/// let template = Template::render("index", &context);
|
||||
/// # assert_eq!(template.to_string(), "");
|
||||
pub fn render<S, T>(name: S, context: &T) -> Template
|
||||
where S: AsRef<str>, T: Serialize
|
||||
/// # #[allow(unused_variables)]
|
||||
/// let template = Template::render("index", context);
|
||||
#[inline]
|
||||
pub fn render<S, C>(name: S, context: C) -> Template
|
||||
where S: Into<Cow<'static, str>>, C: Serialize
|
||||
{
|
||||
let name = name.as_ref();
|
||||
let template = TEMPLATES.get(name);
|
||||
if template.is_none() {
|
||||
let names: Vec<_> = TEMPLATES.keys().map(|s| s.as_str()).collect();
|
||||
Template { name: name.into(), value: to_value(context).ok() }
|
||||
}
|
||||
|
||||
/// Render the template named `name` located at the path `root` with the
|
||||
/// context `context` into a `String`. This method is _very slow_ and should
|
||||
/// **not** be used in any running Rocket application. This method should
|
||||
/// only be used during testing to validate `Template` responses. For other
|
||||
/// uses, use [`render`](#method.render) instead.
|
||||
///
|
||||
/// The `context` can be of any type that implements `Serialize`. This is
|
||||
/// typically a `HashMap` or a custom `struct`. The path `root` can be
|
||||
/// relative, in which case it is relative to the current working directory,
|
||||
/// or absolute.
|
||||
///
|
||||
/// Returns `Some` if the template could be rendered. Otherwise, returns
|
||||
/// `None`. If rendering fails, error output is printed to the console.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::collections::HashMap;
|
||||
/// use rocket_contrib::Template;
|
||||
///
|
||||
/// // Create a `context`. Here, just an empty `HashMap`.
|
||||
/// let mut context = HashMap::new();
|
||||
///
|
||||
/// # context.insert("test", "test");
|
||||
/// # #[allow(unused_variables)]
|
||||
/// let template = Template::show("templates/", "index", context);
|
||||
#[inline]
|
||||
pub fn show<P, S, C>(root: P, name: S, context: C) -> Option<String>
|
||||
where P: AsRef<Path>, S: Into<Cow<'static, str>>, C: Serialize
|
||||
{
|
||||
let root = root.as_ref().to_path_buf();
|
||||
Context::initialize(root).and_then(|ctxt| {
|
||||
Template::render(name, context).finalize(&ctxt).ok().map(|v| v.0)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finalize(self, ctxt: &Context) -> Result<(String, ContentType), Status> {
|
||||
let name = &*self.name;
|
||||
let info = ctxt.templates.get(name).ok_or_else(|| {
|
||||
let ts: Vec<_> = ctxt.templates.keys().map(|s| s.as_str()).collect();
|
||||
error_!("Template '{}' does not exist.", name);
|
||||
info_!("Known templates: {}", names.join(","));
|
||||
info_!("Searched in '{:?}'.", *TEMPLATE_DIR);
|
||||
return Template(None, None);
|
||||
}
|
||||
info_!("Known templates: {}", ts.join(","));
|
||||
info_!("Searched in '{:?}'.", ctxt.root);
|
||||
Status::InternalServerError
|
||||
})?;
|
||||
|
||||
// Keep this set in-sync with the `engine_set` invocation. The macro
|
||||
// `return`s a `Template` if the extenion in `template` matches an
|
||||
// engine in the set. Otherwise, control will fall through.
|
||||
render_set!(name, template.unwrap(), context,
|
||||
"tera_templates" => tera_templates,
|
||||
"handlebars_templates" => handlebars_templates,
|
||||
);
|
||||
let value = self.value.ok_or_else(|| {
|
||||
error_!("The provided template context failed to serialize.");
|
||||
Status::InternalServerError
|
||||
})?;
|
||||
|
||||
unreachable!("A template extension was discovered but not rendered.")
|
||||
let string = ctxt.engines.render(name, &info, value).ok_or_else(|| {
|
||||
error_!("Template '{}' failed to render.", name);
|
||||
Status::InternalServerError
|
||||
})?;
|
||||
|
||||
Ok((string, info.data_type.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,155 +263,15 @@ impl Template {
|
|||
/// extension and a fixed-size body containing the rendered template. If
|
||||
/// rendering fails, an `Err` of `Status::InternalServerError` is returned.
|
||||
impl Responder<'static> for Template {
|
||||
fn respond(self) -> response::Result<'static> {
|
||||
let content_type = match self.1 {
|
||||
Some(ref ext) => ContentType::from_extension(ext),
|
||||
None => ContentType::HTML
|
||||
};
|
||||
fn respond_to(self, req: &Request) -> response::Result<'static> {
|
||||
let ctxt = req.guard::<State<Context>>().succeeded().ok_or_else(|| {
|
||||
error_!("Uninitialized template context: missing fairing.");
|
||||
info_!("To use templates, you must attach `Template::fairing()`.");
|
||||
info_!("See the `Template` documentation for more information.");
|
||||
Status::InternalServerError
|
||||
})?;
|
||||
|
||||
match self.0 {
|
||||
Some(render) => Content(content_type, render).respond(),
|
||||
None => Err(Status::InternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders `self`. If the template cannot be rendered, nothing is written.
|
||||
impl fmt::Display for Template {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
Some(ref render) => render.fmt(f),
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the file path's extension or does nothing if there is none.
|
||||
fn remove_extension<P: AsRef<Path>>(path: P) -> PathBuf {
|
||||
let path = path.as_ref();
|
||||
let stem = match path.file_stem() {
|
||||
Some(stem) => stem,
|
||||
None => return path.to_path_buf()
|
||||
};
|
||||
|
||||
match path.parent() {
|
||||
Some(parent) => parent.join(stem),
|
||||
None => PathBuf::from(stem)
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits a path into a relative path from TEMPLATE_DIR, a name that
|
||||
/// may be used to identify the template, and the template's data type.
|
||||
fn split_path(path: &Path) -> (PathBuf, String, Option<String>) {
|
||||
let rel_path = path.strip_prefix(&*TEMPLATE_DIR).unwrap().to_path_buf();
|
||||
let path_no_ext = remove_extension(&rel_path);
|
||||
let data_type = path_no_ext.extension();
|
||||
let mut name = remove_extension(&path_no_ext).to_string_lossy().into_owned();
|
||||
|
||||
// Ensure template name consistency on Windows systems
|
||||
if cfg!(windows) {
|
||||
name = name.replace("\\", "/");
|
||||
}
|
||||
|
||||
(rel_path, name, data_type.map(|d| d.to_string_lossy().into_owned()))
|
||||
}
|
||||
|
||||
|
||||
/// Returns a HashMap of `TemplateInfo`'s for all of the templates in
|
||||
/// `TEMPLATE_DIR`. Templates are all files that match one of the extensions for
|
||||
/// engine's in `engine_set`.
|
||||
///
|
||||
/// **WARNING:** This function should be called ONCE from a SINGLE THREAD.
|
||||
fn discover_templates() -> HashMap<String, TemplateInfo> {
|
||||
// Keep this set in-sync with the `render_set` invocation.
|
||||
let engines = engine_set![
|
||||
"tera_templates" => tera_templates,
|
||||
"handlebars_templates" => handlebars_templates,
|
||||
];
|
||||
|
||||
let mut templates: HashMap<String, TemplateInfo> = HashMap::new();
|
||||
for &(ext, _) in &engines {
|
||||
let mut glob_path: PathBuf = TEMPLATE_DIR.join("**").join("*");
|
||||
glob_path.set_extension(ext);
|
||||
for path in glob(glob_path.to_str().unwrap()).unwrap().filter_map(Result::ok) {
|
||||
let (rel_path, name, data_type) = split_path(&path);
|
||||
if let Some(info) = templates.get(&*name) {
|
||||
warn_!("Template name '{}' does not have a unique path.", name);
|
||||
info_!("Existing path: {:?}", info.full_path);
|
||||
info_!("Additional path: {:?}", path);
|
||||
warn_!("Using existing path for template '{}'.", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
templates.insert(name, TemplateInfo {
|
||||
full_path: path.to_path_buf(),
|
||||
path: rel_path,
|
||||
extension: ext.to_string(),
|
||||
data_type: data_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for &(ext, register_fn) in &engines {
|
||||
let named_templates = templates.iter()
|
||||
.filter(|&(_, i)| i.extension == ext)
|
||||
.map(|(k, i)| (k.as_str(), i))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
unsafe { register_fn(&*named_templates); }
|
||||
};
|
||||
|
||||
templates
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Combines a `relative_path` and the `TEMPLATE_DIR` path into a full path.
|
||||
fn template_path(relative_path: &str) -> PathBuf {
|
||||
let mut path = PathBuf::from(&*TEMPLATE_DIR);
|
||||
path.push(relative_path);
|
||||
path
|
||||
}
|
||||
|
||||
/// Returns the template system name, given a relative path to a file.
|
||||
fn relative_path_to_name(relative_path: &str) -> String {
|
||||
let path = template_path(relative_path);
|
||||
let (_, name, _) = split_path(&path);
|
||||
name
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_path_index_html() {
|
||||
let path = template_path("index.html.hbs");
|
||||
let (rel_path, name, data_type) = split_path(&path);
|
||||
|
||||
assert_eq!(rel_path.to_string_lossy(), "index.html.hbs");
|
||||
assert_eq!(name, "index");
|
||||
assert_eq!(data_type, Some("html".to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_path_subdir_index_html() {
|
||||
let path = template_path("subdir/index.html.hbs");
|
||||
let (rel_path, name, data_type) = split_path(&path);
|
||||
|
||||
assert_eq!(rel_path.to_string_lossy(), "subdir/index.html.hbs");
|
||||
assert_eq!(name, "subdir/index");
|
||||
assert_eq!(data_type, Some("html".to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_path_doc_examples() {
|
||||
assert_eq!(relative_path_to_name("index.html.hbs"), "index");
|
||||
assert_eq!(relative_path_to_name("index.tera"), "index");
|
||||
assert_eq!(relative_path_to_name("index.hbs"), "index");
|
||||
assert_eq!(relative_path_to_name("dir/index.hbs"), "dir/index");
|
||||
assert_eq!(relative_path_to_name("dir/index.html.tera"), "dir/index");
|
||||
assert_eq!(relative_path_to_name("index.template.html.hbs"),
|
||||
"index.template");
|
||||
assert_eq!(relative_path_to_name("subdir/index.template.html.hbs"),
|
||||
"subdir/index.template");
|
||||
let (render, content_type) = self.finalize(&ctxt)?;
|
||||
Content(content_type, render).respond_to(req)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,67 +1,52 @@
|
|||
extern crate tera;
|
||||
|
||||
use super::serde::Serialize;
|
||||
use super::TemplateInfo;
|
||||
use super::{Engine, TemplateInfo};
|
||||
|
||||
use self::tera::Tera;
|
||||
pub use self::tera::Tera;
|
||||
|
||||
static mut TERA: Option<Tera> = None;
|
||||
impl Engine for Tera {
|
||||
const EXT: &'static str = "tera";
|
||||
|
||||
pub const EXT: &'static str = "tera";
|
||||
fn init(templates: &[(&str, &TemplateInfo)]) -> Option<Tera> {
|
||||
// Create the Tera instance.
|
||||
let mut tera = Tera::default();
|
||||
let ext = [".html.tera", ".htm.tera", ".xml.tera", ".html", ".htm", ".xml"];
|
||||
tera.autoescape_on(ext.to_vec());
|
||||
|
||||
// This function must be called a SINGLE TIME from A SINGLE THREAD for safety to
|
||||
// hold here and in `render`.
|
||||
pub unsafe fn register(templates: &[(&str, &TemplateInfo)]) -> bool {
|
||||
if TERA.is_some() {
|
||||
error_!("Internal error: reregistering Tera!");
|
||||
return false;
|
||||
}
|
||||
// Collect into a tuple of (name, path) for Tera.
|
||||
let tera_templates = templates.iter()
|
||||
.map(|&(name, info)| (&info.path, Some(name)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut tera = Tera::default();
|
||||
let ext = [".html.tera", ".htm.tera", ".xml.tera", ".html", ".htm", ".xml"];
|
||||
tera.autoescape_on(ext.to_vec());
|
||||
|
||||
// Collect into a tuple of (name, path) for Tera.
|
||||
let tera_templates = templates.iter()
|
||||
.map(|&(name, info)| (&info.full_path, Some(name)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Finally try to tell Tera about all of the templates.
|
||||
let mut success = true;
|
||||
if let Err(e) = tera.add_template_files(tera_templates) {
|
||||
error_!("Failed to initialize Tera templates: {:?}", e);
|
||||
success = false;
|
||||
}
|
||||
|
||||
TERA = Some(tera);
|
||||
success
|
||||
}
|
||||
|
||||
pub fn render<T>(name: &str, _: &TemplateInfo, context: &T) -> Option<String>
|
||||
where T: Serialize
|
||||
{
|
||||
let tera = match unsafe { TERA.as_ref() } {
|
||||
Some(tera) => tera,
|
||||
None => {
|
||||
error_!("Internal error: `render` called before Tera init.");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if tera.get_template(name).is_err() {
|
||||
error_!("Tera template '{}' does not exist.", name);
|
||||
return None;
|
||||
};
|
||||
|
||||
match tera.render(name, context) {
|
||||
Ok(string) => Some(string),
|
||||
Err(e) => {
|
||||
error_!("Error rendering Tera template '{}'.", name);
|
||||
// Finally try to tell Tera about all of the templates.
|
||||
if let Err(e) = tera.add_template_files(tera_templates) {
|
||||
error!("Failed to initialize Tera templating.");
|
||||
for error in e.iter().skip(1) {
|
||||
error_!("{}.", error);
|
||||
info_!("{}.", error);
|
||||
}
|
||||
return None
|
||||
}
|
||||
|
||||
None
|
||||
Some(tera)
|
||||
}
|
||||
|
||||
fn render<C: Serialize>(&self, name: &str, context: C) -> Option<String> {
|
||||
if self.get_template(name).is_err() {
|
||||
error_!("Tera template '{}' does not exist.", name);
|
||||
return None;
|
||||
};
|
||||
|
||||
match self.render(name, &context) {
|
||||
Ok(string) => Some(string),
|
||||
Err(e) => {
|
||||
error_!("Error rendering Tera template '{}'.", name);
|
||||
for error in e.iter().skip(1) {
|
||||
error_!("{}.", error);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,13 @@ extern crate rocket;
|
|||
extern crate rocket_contrib;
|
||||
|
||||
use std::env;
|
||||
use rocket::config::Config;
|
||||
use rocket::config::Environment::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn init() {
|
||||
fn template_root() -> PathBuf {
|
||||
let cwd = env::current_dir().expect("current working directory");
|
||||
let tests_dir = cwd.join("tests");
|
||||
|
||||
let config = Config::build(Development).root(tests_dir).unwrap();
|
||||
rocket::custom(config, true);
|
||||
cwd.join("tests").join("templates")
|
||||
}
|
||||
|
||||
// FIXME: Do something about overlapping configs.
|
||||
#[cfg(feature = "tera_templates")]
|
||||
mod tera_tests {
|
||||
use super::*;
|
||||
|
@ -27,19 +22,17 @@ mod tera_tests {
|
|||
|
||||
#[test]
|
||||
fn test_tera_templates() {
|
||||
init();
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert("title", "_test_");
|
||||
map.insert("content", "<script />");
|
||||
|
||||
// Test with a txt file, which shouldn't escape.
|
||||
let template = Template::render("tera/txt_test", &map);
|
||||
assert_eq!(&template.to_string(), UNESCAPED_EXPECTED);
|
||||
let template = Template::show(template_root(), "tera/txt_test", &map);
|
||||
assert_eq!(template, Some(UNESCAPED_EXPECTED.into()));
|
||||
|
||||
// Now with an HTML file, which should.
|
||||
let template = Template::render("tera/html_test", &map);
|
||||
assert_eq!(&template.to_string(), ESCAPED_EXPECTED);
|
||||
let template = Template::show(template_root(), "tera/html_test", &map);
|
||||
assert_eq!(template, Some(ESCAPED_EXPECTED.into()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,15 +47,13 @@ mod handlebars_tests {
|
|||
|
||||
#[test]
|
||||
fn test_handlebars_templates() {
|
||||
init();
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert("title", "_test_");
|
||||
map.insert("content", "<script /> hi");
|
||||
|
||||
// Test with a txt file, which shouldn't escape.
|
||||
let template = Template::render("hbs/test", &map);
|
||||
assert_eq!(&template.to_string(), EXPECTED);
|
||||
let template = Template::show(template_root(), "hbs/test", &map);
|
||||
assert_eq!(template, Some(EXPECTED.into()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("/")]
|
||||
#[allow(unmounted_route)]
|
||||
pub fn hello() -> &'static str {
|
||||
"Hello, world!"
|
||||
}
|
|
@ -2,9 +2,8 @@
|
|||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
extern crate config;
|
||||
|
||||
// This example's illustration is the Rocket.toml file.
|
||||
fn main() {
|
||||
rocket::ignite().mount("/hello", routes![config::hello]).launch();
|
||||
rocket::ignite().launch();
|
||||
}
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
extern crate rocket;
|
||||
extern crate config as lib;
|
||||
|
||||
use rocket::config::{self, Environment};
|
||||
use rocket::http::Method;
|
||||
use rocket::{self, State};
|
||||
use rocket::fairing::AdHoc;
|
||||
use rocket::config::{self, Config, Environment};
|
||||
use rocket::http::{Method, Status};
|
||||
use rocket::LoggingLevel;
|
||||
use rocket::testing::MockRequest;
|
||||
|
||||
pub fn test_config(environment: Environment) {
|
||||
// Manually set the config environment variable. Rocket will initialize the
|
||||
// environment in `ignite()`.
|
||||
::std::env::set_var("ROCKET_ENV", environment.to_string());
|
||||
rocket::ignite().mount("/hello", routes![lib::hello]);
|
||||
struct LocalConfig(Config);
|
||||
|
||||
// Get the active environment and ensure that it matches our expectations.
|
||||
let config = config::active().unwrap();
|
||||
match environment {
|
||||
Environment::Development => {
|
||||
#[get("/check_config")]
|
||||
fn check_config(config: State<LocalConfig>) -> Option<()> {
|
||||
let environment = match ::std::env::var("ROCKET_ENV") {
|
||||
Ok(name) => name,
|
||||
Err(_) => return None
|
||||
};
|
||||
|
||||
let config = &config.0;
|
||||
match &*environment {
|
||||
"development" => {
|
||||
assert_eq!(config.address, "localhost".to_string());
|
||||
assert_eq!(config.port, 8000);
|
||||
assert_eq!(config.workers, 1);
|
||||
|
@ -25,7 +26,7 @@ pub fn test_config(environment: Environment) {
|
|||
assert_eq!(config.get_str("hi"), Ok("Hello!"));
|
||||
assert_eq!(config.get_bool("is_extra"), Ok(true));
|
||||
}
|
||||
Environment::Staging => {
|
||||
"staging" => {
|
||||
assert_eq!(config.address, "0.0.0.0".to_string());
|
||||
assert_eq!(config.port, 80);
|
||||
assert_eq!(config.workers, 8);
|
||||
|
@ -33,7 +34,7 @@ pub fn test_config(environment: Environment) {
|
|||
assert_eq!(config.environment, config::Environment::Staging);
|
||||
assert_eq!(config.extras().count(), 0);
|
||||
}
|
||||
Environment::Production => {
|
||||
"production" => {
|
||||
assert_eq!(config.address, "0.0.0.0".to_string());
|
||||
assert_eq!(config.port, 80);
|
||||
assert_eq!(config.workers, 12);
|
||||
|
@ -41,13 +42,30 @@ pub fn test_config(environment: Environment) {
|
|||
assert_eq!(config.environment, config::Environment::Production);
|
||||
assert_eq!(config.extras().count(), 0);
|
||||
}
|
||||
_ => {
|
||||
panic!("Unknown environment in envvar: {}", environment);
|
||||
}
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn test_hello() {
|
||||
let rocket = rocket::ignite().mount("/hello", routes![lib::hello]);
|
||||
let mut request = MockRequest::new(Method::Get, "/hello");
|
||||
let mut response = request.dispatch_with(&rocket);
|
||||
pub fn test_config(environment: Environment) {
|
||||
// Manually set the config environment variable. Rocket will initialize the
|
||||
// environment in `ignite()`. We'll read this back in the handler to config.
|
||||
::std::env::set_var("ROCKET_ENV", environment.to_string());
|
||||
|
||||
assert_eq!(response.body_string(), Some("Hello, world!".into()));
|
||||
// FIXME: launch fairings aren't run during tests since...the Rocket isn't
|
||||
// being launch
|
||||
let rocket = rocket::ignite()
|
||||
.attach(AdHoc::on_attach(|rocket| {
|
||||
println!("Attaching local config.");
|
||||
let config = rocket.config().clone();
|
||||
Ok(rocket.manage(LocalConfig(config)))
|
||||
}))
|
||||
.mount("/", routes![check_config]);
|
||||
|
||||
let mut request = MockRequest::new(Method::Get, "/check_config");
|
||||
let response = request.dispatch_with(&rocket);
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
}
|
||||
|
|
|
@ -8,5 +8,4 @@ mod common;
|
|||
#[test]
|
||||
fn test_development_config() {
|
||||
common::test_config(rocket::config::Environment::Development);
|
||||
common::test_hello();
|
||||
}
|
||||
|
|
|
@ -8,5 +8,4 @@ mod common;
|
|||
#[test]
|
||||
fn test_production_config() {
|
||||
common::test_config(rocket::config::Environment::Production);
|
||||
common::test_hello();
|
||||
}
|
||||
|
|
|
@ -8,5 +8,4 @@ mod common;
|
|||
#[test]
|
||||
fn test_staging_config() {
|
||||
common::test_config(rocket::config::Environment::Staging);
|
||||
common::test_hello();
|
||||
}
|
||||
|
|
|
@ -36,6 +36,10 @@ fn index(cookies: Cookies) -> Template {
|
|||
Template::render("index", &context)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rocket::ignite().mount("/", routes![submit, index]).launch();
|
||||
fn rocket() -> rocket::Rocket {
|
||||
rocket::ignite().mount("/", routes![submit, index]).attach(Template::fairing())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rocket().launch();
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ use rocket::testing::MockRequest;
|
|||
use rocket::http::*;
|
||||
use rocket_contrib::Template;
|
||||
|
||||
const TEMPLATE_ROOT: &'static str = "templates/";
|
||||
|
||||
#[test]
|
||||
fn test_submit() {
|
||||
let rocket = rocket::ignite().mount("/", routes![super::index, super::submit]);
|
||||
let rocket = rocket();
|
||||
let mut request = MockRequest::new(Method::Post, "/submit")
|
||||
.header(ContentType::Form)
|
||||
.body("message=Hello from Rocket!");
|
||||
|
@ -21,7 +23,7 @@ fn test_submit() {
|
|||
}
|
||||
|
||||
fn test_body(optional_cookie: Option<Cookie<'static>>, expected_body: String) {
|
||||
let rocket = rocket::ignite().mount("/", routes![super::index, super::submit]);
|
||||
let rocket = rocket();
|
||||
let mut request = MockRequest::new(Method::Get, "/");
|
||||
|
||||
// Attach a cookie if one is given.
|
||||
|
@ -38,16 +40,16 @@ fn test_body(optional_cookie: Option<Cookie<'static>>, expected_body: String) {
|
|||
fn test_index() {
|
||||
// Render the template with an empty context to test against.
|
||||
let mut context: HashMap<&str, &str> = HashMap::new();
|
||||
let template = Template::render("index", &context);
|
||||
let template = Template::show(TEMPLATE_ROOT, "index", &context).unwrap();
|
||||
|
||||
// Test the route without sending the "message" cookie.
|
||||
test_body(None, template.to_string());
|
||||
test_body(None, template);
|
||||
|
||||
// Render the template with a context that contains the message.
|
||||
context.insert("message", "Hello from Rocket!");
|
||||
|
||||
// Test the route with the "message" cookie.
|
||||
let cookie = Cookie::new("message", "Hello from Rocket!");
|
||||
let template = Template::render("index", &context);
|
||||
test_body(Some(cookie), template.to_string());
|
||||
let template = Template::show(TEMPLATE_ROOT, "index", &context).unwrap();
|
||||
test_body(Some(cookie), template);
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ fn rocket() -> rocket::Rocket {
|
|||
let token_val = rocket.config().get_int("token").unwrap_or(-1);
|
||||
Ok(rocket.manage(Token(token_val)))
|
||||
}))
|
||||
.attach(AdHoc::on_launch(|rocket| {
|
||||
.attach(AdHoc::on_launch(|_| {
|
||||
println!("Rocket is about to launch!");
|
||||
}))
|
||||
.attach(AdHoc::on_request(|req, _| {
|
||||
|
|
|
@ -40,9 +40,13 @@ fn not_found(req: &Request) -> Template {
|
|||
Template::render("error/404", &map)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn rocket() -> rocket::Rocket {
|
||||
rocket::ignite()
|
||||
.mount("/", routes![index, get])
|
||||
.attach(Template::fairing())
|
||||
.catch(errors![not_found])
|
||||
.launch();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rocket().launch();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
use rocket;
|
||||
use super::rocket;
|
||||
use rocket::testing::MockRequest;
|
||||
use rocket::http::Method::*;
|
||||
use rocket::http::Status;
|
||||
use rocket::Response;
|
||||
use rocket_contrib::Template;
|
||||
|
||||
const TEMPLATE_ROOT: &'static str = "templates/";
|
||||
|
||||
macro_rules! run_test {
|
||||
($req:expr, $test_fn:expr) => ({
|
||||
let rocket = rocket::ignite()
|
||||
.mount("/", routes![super::index, super::get])
|
||||
.catch(errors![super::not_found]);
|
||||
|
||||
let rocket = rocket();
|
||||
let mut req = $req;
|
||||
$test_fn(req.dispatch_with(&rocket));
|
||||
})
|
||||
|
@ -25,8 +24,8 @@ fn test_root() {
|
|||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert!(response.body().is_none());
|
||||
|
||||
let location_headers: Vec<_> = response.headers().get("Location").collect();
|
||||
assert_eq!(location_headers, vec!["/hello/Unknown"]);
|
||||
let location: Vec<_> = response.headers().get("Location").collect();
|
||||
assert_eq!(location, vec!["/hello/Unknown"]);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -36,10 +35,10 @@ fn test_root() {
|
|||
run_test!(req, |mut response: Response| {
|
||||
let mut map = ::std::collections::HashMap::new();
|
||||
map.insert("path", "/");
|
||||
let expected_body = Template::render("error/404", &map).to_string();
|
||||
let expected = Template::show(TEMPLATE_ROOT, "error/404", &map).unwrap();
|
||||
|
||||
assert_eq!(response.status(), Status::NotFound);
|
||||
assert_eq!(response.body_string(), Some(expected_body));
|
||||
assert_eq!(response.body_string(), Some(expected));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +55,7 @@ fn test_name() {
|
|||
items: vec!["One", "Two", "Three"].iter().map(|s| s.to_string()).collect()
|
||||
};
|
||||
|
||||
let expected = Template::render("index", &context).to_string();
|
||||
let expected = Template::show(TEMPLATE_ROOT, "index", &context).unwrap();
|
||||
assert_eq!(response.body_string(), Some(expected));
|
||||
});
|
||||
}
|
||||
|
@ -70,7 +69,8 @@ fn test_404() {
|
|||
|
||||
let mut map = ::std::collections::HashMap::new();
|
||||
map.insert("path", "/hello/");
|
||||
let expected = Template::render("error/404", &map).to_string();
|
||||
|
||||
let expected = Template::show(TEMPLATE_ROOT, "error/404", &map).unwrap();
|
||||
assert_eq!(response.body_string(), Some(expected));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<h3>Here are your items:</h3>
|
||||
<ul>
|
||||
{{#each items}}
|
||||
<li>{{this}}</li>
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -17,13 +17,13 @@ fn forward(_req: &Request, data: Data) -> Outcome<'static> {
|
|||
Outcome::forward(data)
|
||||
}
|
||||
|
||||
fn hi(_req: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::of("Hello!")
|
||||
fn hi(req: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::from(req, "Hello!")
|
||||
}
|
||||
|
||||
fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> {
|
||||
let param = req.get_param::<&'a RawStr>(0);
|
||||
Outcome::of(param.map(|r| r.as_str()).unwrap_or("unnamed"))
|
||||
Outcome::from(req, param.map(|r| r.as_str()).unwrap_or("unnamed"))
|
||||
}
|
||||
|
||||
fn echo_url(req: &Request, _: Data) -> Outcome<'static> {
|
||||
|
@ -32,7 +32,7 @@ fn echo_url(req: &Request, _: Data) -> Outcome<'static> {
|
|||
.split_at(6)
|
||||
.1;
|
||||
|
||||
Outcome::of(RawStr::from_str(param).url_decode())
|
||||
Outcome::from(req, RawStr::from_str(param).url_decode())
|
||||
}
|
||||
|
||||
fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
||||
|
@ -44,7 +44,7 @@ fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
|||
let file = File::create("/tmp/upload.txt");
|
||||
if let Ok(mut file) = file {
|
||||
if let Ok(n) = io::copy(&mut data.open(), &mut file) {
|
||||
return Outcome::of(format!("OK: {} bytes uploaded.", n));
|
||||
return Outcome::from(req, format!("OK: {} bytes uploaded.", n));
|
||||
}
|
||||
|
||||
println!(" => Failed copying.");
|
||||
|
@ -55,12 +55,13 @@ fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_upload(_: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::of(File::open("/tmp/upload.txt").ok())
|
||||
fn get_upload(req: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::from(req, File::open("/tmp/upload.txt").ok())
|
||||
}
|
||||
|
||||
fn not_found_handler<'r>(_: Error, req: &'r Request) -> response::Result<'r> {
|
||||
Custom(Status::NotFound, format!("Couldn't find: {}", req.uri())).respond()
|
||||
let res = Custom(Status::NotFound, format!("Couldn't find: {}", req.uri()));
|
||||
res.respond_to(req)
|
||||
}
|
||||
|
||||
fn rocket() -> rocket::Rocket {
|
||||
|
|
|
@ -32,7 +32,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Conn {
|
|||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Conn, ()> {
|
||||
let pool = match <State<Pool> as FromRequest>::from_request(request) {
|
||||
let pool = match request.guard::<State<Pool>>() {
|
||||
Outcome::Success(pool) => pool,
|
||||
Outcome::Failure(e) => return Outcome::Failure(e),
|
||||
Outcome::Forward(_) => return Outcome::Forward(()),
|
||||
|
|
|
@ -76,5 +76,6 @@ fn main() {
|
|||
.manage(db::init_pool())
|
||||
.mount("/", routes![index, static_files::all])
|
||||
.mount("/todo/", routes![new, toggle, delete])
|
||||
.attach(Template::fairing())
|
||||
.launch();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
class="u-full-width {% if msg %}field-{{msg.0}}{% endif %}" />
|
||||
{% if msg %}
|
||||
<small class="field-{{msg.0}}-msg">
|
||||
{{ msg.1 }}
|
||||
{{ msg.1 }}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -83,11 +83,12 @@ impl Catcher {
|
|||
/// use rocket::http::Status;
|
||||
///
|
||||
/// fn handle_404<'r>(_: Error, req: &'r Request) -> Result<'r> {
|
||||
/// Custom(Status::NotFound, format!("Couldn't find: {}", req.uri())).respond()
|
||||
/// let res = Custom(Status::NotFound, format!("404: {}", req.uri()));
|
||||
/// res.respond_to(req)
|
||||
/// }
|
||||
///
|
||||
/// fn handle_500<'r>(_: Error, _: &'r Request) -> Result<'r> {
|
||||
/// "Whoops, we messed up!".respond()
|
||||
/// fn handle_500<'r>(_: Error, req: &'r Request) -> Result<'r> {
|
||||
/// "Whoops, we messed up!".respond_to(req)
|
||||
/// }
|
||||
///
|
||||
/// let not_found_catcher = Catcher::new(404, handle_404);
|
||||
|
@ -156,10 +157,10 @@ macro_rules! default_errors {
|
|||
let mut map = HashMap::new();
|
||||
|
||||
$(
|
||||
fn $fn_name<'r>(_: Error, _r: &'r Request) -> response::Result<'r> {
|
||||
fn $fn_name<'r>(_: Error, req: &'r Request) -> response::Result<'r> {
|
||||
status::Custom(Status::from_code($code).unwrap(),
|
||||
content::HTML(error_page_template!($code, $name, $description))
|
||||
).respond()
|
||||
).respond_to(req)
|
||||
}
|
||||
|
||||
map.insert($code, Catcher::new_default($code, $fn_name));
|
||||
|
|
|
@ -57,7 +57,7 @@ macro_rules! config_from_raw {
|
|||
$($key:ident => ($type:ident, $set:ident, $map:expr)),+ | _ => $rest:expr) => (
|
||||
match $name {
|
||||
$(stringify!($key) => {
|
||||
concat_idents!(value_as_, $type)($config, $name, $value)
|
||||
super::custom_values::$type($config, $name, $value)
|
||||
.and_then(|parsed| $map($config.$set(parsed)))
|
||||
})+
|
||||
_ => $rest
|
||||
|
|
|
@ -50,7 +50,8 @@ pub struct Limits {
|
|||
|
||||
impl Default for Limits {
|
||||
fn default() -> Limits {
|
||||
Limits { forms: 1024 * 32, extra: Vec::new() }
|
||||
/// Default limit for forms is 32KiB.
|
||||
Limits { forms: 32 * 1024, extra: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,33 +104,33 @@ impl fmt::Display for Limits {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn value_as_str<'a>(conf: &Config, name: &str, v: &'a Value) -> Result<&'a str> {
|
||||
pub fn str<'a>(conf: &Config, name: &str, v: &'a Value) -> Result<&'a str> {
|
||||
v.as_str().ok_or(conf.bad_type(name, v.type_str(), "a string"))
|
||||
}
|
||||
|
||||
pub fn value_as_u64(conf: &Config, name: &str, value: &Value) -> Result<u64> {
|
||||
pub fn u64(conf: &Config, name: &str, value: &Value) -> Result<u64> {
|
||||
match value.as_integer() {
|
||||
Some(x) if x >= 0 => Ok(x as u64),
|
||||
_ => Err(conf.bad_type(name, value.type_str(), "an unsigned integer"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_as_u16(conf: &Config, name: &str, value: &Value) -> Result<u16> {
|
||||
pub fn u16(conf: &Config, name: &str, value: &Value) -> Result<u16> {
|
||||
match value.as_integer() {
|
||||
Some(x) if x >= 0 && x <= (u16::max_value() as i64) => Ok(x as u16),
|
||||
_ => Err(conf.bad_type(name, value.type_str(), "a 16-bit unsigned integer"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_as_log_level(conf: &Config,
|
||||
pub fn log_level(conf: &Config,
|
||||
name: &str,
|
||||
value: &Value
|
||||
) -> Result<LoggingLevel> {
|
||||
value_as_str(conf, name, value)
|
||||
str(conf, name, value)
|
||||
.and_then(|s| s.parse().map_err(|e| conf.bad_type(name, value.type_str(), e)))
|
||||
}
|
||||
|
||||
pub fn value_as_tls_config<'v>(conf: &Config,
|
||||
pub fn tls_config<'v>(conf: &Config,
|
||||
name: &str,
|
||||
value: &'v Value,
|
||||
) -> Result<(&'v str, &'v str)> {
|
||||
|
@ -140,8 +141,8 @@ pub fn value_as_tls_config<'v>(conf: &Config,
|
|||
let env = conf.environment;
|
||||
for (key, value) in table {
|
||||
match key.as_str() {
|
||||
"certs" => certs_path = Some(value_as_str(conf, "tls.certs", value)?),
|
||||
"key" => key_path = Some(value_as_str(conf, "tls.key", value)?),
|
||||
"certs" => certs_path = Some(str(conf, "tls.certs", value)?),
|
||||
"key" => key_path = Some(str(conf, "tls.key", value)?),
|
||||
_ => return Err(ConfigError::UnknownKey(format!("{}.tls.{}", env, key)))
|
||||
}
|
||||
}
|
||||
|
@ -154,13 +155,13 @@ pub fn value_as_tls_config<'v>(conf: &Config,
|
|||
}
|
||||
}
|
||||
|
||||
pub fn value_as_limits(conf: &Config, name: &str, value: &Value) -> Result<Limits> {
|
||||
pub fn limits(conf: &Config, name: &str, value: &Value) -> Result<Limits> {
|
||||
let table = value.as_table()
|
||||
.ok_or_else(|| conf.bad_type(name, value.type_str(), "a table"))?;
|
||||
|
||||
let mut limits = Limits::default();
|
||||
for (key, val) in table {
|
||||
let val = value_as_u64(conf, &format!("limits.{}", key), val)?;
|
||||
let val = u64(conf, &format!("limits.{}", key), val)?;
|
||||
limits = limits.add(key.as_str(), val);
|
||||
}
|
||||
|
||||
|
|
|
@ -158,28 +158,33 @@
|
|||
//! ## Retrieving Configuration Parameters
|
||||
//!
|
||||
//! Configuration parameters for the currently active configuration environment
|
||||
//! can be retrieved via the [active](fn.active.html) function and methods on
|
||||
//! the [Config](struct.Config.html) structure. The general structure is to call
|
||||
//! `active` and then one of the `get_` methods on the returned `Config`
|
||||
//! structure.
|
||||
//! can be retrieved via the [config](/rocket/struct.Rocket.html#method.config)
|
||||
//! method on an instance of `Rocket` and `get_` methods on the
|
||||
//! [Config](struct.Config.html) structure.
|
||||
//!
|
||||
//! As an example, consider the following code used by the `Template` type to
|
||||
//! retrieve the value of the `template_dir` configuration parameter. If the
|
||||
//! value isn't present or isn't a string, a default value is used.
|
||||
//! The retrivial of configuration parameters usually occurs at launch time via
|
||||
//! a [launch fairing](/rocket/fairing/trait.Fairing.html). If information about
|
||||
//! the configuraiton is needed later in the program, an attach fairing can be
|
||||
//! used to store the information as managed state. As an example of the latter,
|
||||
//! consider the following short program which reads the `token` configuration
|
||||
//! parameter and stores the value or a default in a `Token` managed state
|
||||
//! value:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::path::PathBuf;
|
||||
//! use rocket::config::{self, ConfigError};
|
||||
//! use rocket::fairing::AdHoc;
|
||||
//!
|
||||
//! const DEFAULT_TEMPLATE_DIR: &'static str = "templates";
|
||||
//! struct Token(i64);
|
||||
//!
|
||||
//! # #[allow(unused_variables)]
|
||||
//! let template_dir = config::active().ok_or(ConfigError::NotFound)
|
||||
//! .map(|config| config.root().join(DEFAULT_TEMPLATE_DIR))
|
||||
//! .unwrap_or_else(|_| PathBuf::from(DEFAULT_TEMPLATE_DIR));
|
||||
//! fn main() {
|
||||
//! rocket::ignite()
|
||||
//! .attach(AdHoc::on_attach(|rocket| {
|
||||
//! println!("Adding token managed state from config...");
|
||||
//! let token_val = rocket.config().get_int("token").unwrap_or(-1);
|
||||
//! Ok(rocket.manage(Token(token_val)))
|
||||
//! }))
|
||||
//! # ;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Libraries should always use a default if a parameter is not defined.
|
||||
|
||||
mod error;
|
||||
mod environment;
|
||||
|
@ -188,7 +193,6 @@ mod builder;
|
|||
mod toml_ext;
|
||||
mod custom_values;
|
||||
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
use std::fs::{self, File};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
|
@ -213,9 +217,6 @@ use self::toml_ext::parse_simple_toml_value;
|
|||
use logger::{self, LoggingLevel};
|
||||
use http::uncased::uncased_eq;
|
||||
|
||||
static INIT: Once = ONCE_INIT;
|
||||
static mut CONFIG: Option<RocketConfig> = None;
|
||||
|
||||
const CONFIG_FILENAME: &'static str = "Rocket.toml";
|
||||
const GLOBAL_ENV_NAME: &'static str = "global";
|
||||
const ENV_VAR_PREFIX: &'static str = "ROCKET_";
|
||||
|
@ -466,32 +467,7 @@ impl RocketConfig {
|
|||
/// # Panics
|
||||
///
|
||||
/// If there is a problem, prints a nice error message and bails.
|
||||
pub(crate) fn init() -> (&'static Config, bool) {
|
||||
let mut this_init = false;
|
||||
unsafe {
|
||||
INIT.call_once(|| {
|
||||
private_init();
|
||||
this_init = true;
|
||||
});
|
||||
|
||||
(CONFIG.as_ref().unwrap().active(), this_init)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn custom_init(config: Config) -> (&'static Config, bool) {
|
||||
let mut this_init = false;
|
||||
|
||||
unsafe {
|
||||
INIT.call_once(|| {
|
||||
CONFIG = Some(RocketConfig::new(config));
|
||||
this_init = true;
|
||||
});
|
||||
|
||||
(CONFIG.as_ref().unwrap().active(), this_init)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn private_init() {
|
||||
pub(crate) fn init() -> Config {
|
||||
let bail = |e: ConfigError| -> ! {
|
||||
logger::init(LoggingLevel::Debug);
|
||||
e.pretty_print();
|
||||
|
@ -515,16 +491,8 @@ unsafe fn private_init() {
|
|||
RocketConfig::active_default(&default_path).unwrap_or_else(|e| bail(e))
|
||||
});
|
||||
|
||||
CONFIG = Some(config);
|
||||
}
|
||||
|
||||
/// Retrieve the active configuration, if there is one.
|
||||
///
|
||||
/// This function is guaranteed to return `Some` once a Rocket application has
|
||||
/// started. Before a Rocket application has started, or when there is no active
|
||||
/// Rocket application (such as during testing), this function will return None.
|
||||
pub fn active() -> Option<&'static Config> {
|
||||
unsafe { CONFIG.as_ref().map(|c| c.active()) }
|
||||
// FIXME: Should probably store all of the config.
|
||||
config.active().clone()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Types and traits for handling incoming body data.
|
||||
|
||||
mod data;
|
||||
mod data_stream;
|
||||
mod net_stream;
|
||||
|
|
|
@ -24,8 +24,8 @@ pub enum Error {
|
|||
///
|
||||
/// In almost every instance, a launch error occurs because of an I/O error;
|
||||
/// this is represented by the `Io` variant. A launch error may also occur
|
||||
/// because of ill-defined routes that lead to collisions or because a launch
|
||||
/// fairing encounted an error; these are represented by the `Collision` and
|
||||
/// because of ill-defined routes that lead to collisions or because a fairing
|
||||
/// encountered an error; these are represented by the `Collision` and
|
||||
/// `FailedFairing` variants, respectively. The `Unknown` variant captures all
|
||||
/// other kinds of launch errors.
|
||||
#[derive(Debug)]
|
||||
|
@ -196,7 +196,7 @@ impl Drop for LaunchError {
|
|||
panic!("route collisions detected");
|
||||
}
|
||||
LaunchErrorKind::FailedFairing => {
|
||||
error!("Rocket failed to launch due to a failing launch fairing.");
|
||||
error!("Rocket failed to launch due to a failing fairing.");
|
||||
panic!("launch fairing failure");
|
||||
}
|
||||
LaunchErrorKind::Unknown(ref e) => {
|
||||
|
|
|
@ -85,8 +85,8 @@ impl Fairings {
|
|||
use term_painter::ToStyle;
|
||||
use term_painter::Color::{White, Magenta};
|
||||
|
||||
if self.all_fairings.len() > 0 {
|
||||
info!("📡 {}:", Magenta.paint("Fairings"));
|
||||
if self.all_fairings.is_empty() {
|
||||
return
|
||||
}
|
||||
|
||||
fn info_if_nonempty(kind: &str, fairings: &[&Fairing]) {
|
||||
|
@ -97,6 +97,7 @@ impl Fairings {
|
|||
White.paint(names.join(", ")));
|
||||
}
|
||||
|
||||
info!("📡 {}:", Magenta.paint("Fairings"));
|
||||
info_if_nonempty("launch", &self.launch);
|
||||
info_if_nonempty("request", &self.request);
|
||||
info_if_nonempty("response", &self.response);
|
||||
|
|
|
@ -12,8 +12,8 @@ pub type Outcome<'r> = outcome::Outcome<Response<'r>, Status, Data>;
|
|||
|
||||
impl<'r> Outcome<'r> {
|
||||
#[inline]
|
||||
pub fn of<T: Responder<'r>>(responder: T) -> Outcome<'r> {
|
||||
match responder.respond() {
|
||||
pub fn from<T: Responder<'r>>(req: &Request, responder: T) -> Outcome<'r> {
|
||||
match responder.respond_to(req) {
|
||||
Ok(response) => outcome::Outcome::Success(response),
|
||||
Err(status) => outcome::Outcome::Failure(status)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#![feature(lookup_host)]
|
||||
#![feature(plugin)]
|
||||
#![feature(never_type)]
|
||||
#![feature(concat_idents)]
|
||||
#![feature(more_io_inner_methods)]
|
||||
|
||||
#![plugin(pear_codegen)]
|
||||
|
|
|
@ -141,13 +141,20 @@ impl Log for RocketLogger {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn init(level: LoggingLevel) {
|
||||
pub fn try_init(level: LoggingLevel, verbose: bool) {
|
||||
let result = log::set_logger(|max_log_level| {
|
||||
max_log_level.set(level.max_log_level().to_log_level_filter());
|
||||
Box::new(RocketLogger(level))
|
||||
});
|
||||
|
||||
if let Err(err) = result {
|
||||
println!("Logger failed to initialize: {}", err);
|
||||
if verbose {
|
||||
println!("Logger failed to initialize: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn init(level: LoggingLevel) {
|
||||
try_init(level, true)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ use std::marker::PhantomData;
|
|||
use std::fmt::{self, Debug};
|
||||
use std::io::Read;
|
||||
|
||||
use config;
|
||||
use http::Status;
|
||||
use request::Request;
|
||||
use data::{self, Data, FromData};
|
||||
|
@ -270,9 +269,6 @@ impl<'f, T: FromForm<'f> + Debug + 'f> Debug for Form<'f, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Default limit for forms is 32KiB.
|
||||
const LIMIT: u64 = 32 * (1 << 10);
|
||||
|
||||
/// Parses a `Form` from incoming form data.
|
||||
///
|
||||
/// If the content type of the request data is not
|
||||
|
@ -297,7 +293,7 @@ impl<'f, T: FromForm<'f>> FromData for Form<'f, T> where T::Error: Debug {
|
|||
}
|
||||
|
||||
let mut form_string = String::with_capacity(4096);
|
||||
let limit = config::active().map(|c| c.limits.forms).unwrap_or(LIMIT);
|
||||
let limit = request.limits().forms;
|
||||
let mut stream = data.open().take(limit);
|
||||
if let Err(e) = stream.read_to_string(&mut form_string) {
|
||||
error_!("IO Error: {:?}", e);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::fmt::Debug;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use outcome::{self, IntoOutcome};
|
||||
use request::Request;
|
||||
use outcome::{self, IntoOutcome};
|
||||
use outcome::Outcome::*;
|
||||
|
||||
use http::{Status, ContentType, Accept, Method, Cookies, Session};
|
||||
|
|
|
@ -9,17 +9,20 @@ use term_painter::ToStyle;
|
|||
use state::{Container, Storage};
|
||||
|
||||
use error::Error;
|
||||
use super::{FromParam, FromSegments};
|
||||
use super::{FromParam, FromSegments, FromRequest, Outcome};
|
||||
|
||||
use router::Route;
|
||||
use config::{Config, Limits};
|
||||
use http::uri::{URI, Segments};
|
||||
use http::{Method, Header, HeaderMap, Cookies, Session, CookieJar, Key};
|
||||
use http::{Method, Header, HeaderMap, Cookies, Session, CookieJar};
|
||||
use http::{RawStr, ContentType, Accept, MediaType};
|
||||
use http::hyper;
|
||||
|
||||
struct PresetState<'r> {
|
||||
key: &'r Key,
|
||||
managed_state: &'r Container,
|
||||
// The running Rocket instances configuration.
|
||||
config: &'r Config,
|
||||
// The managed state of the running Rocket instance.
|
||||
state: &'r Container,
|
||||
}
|
||||
|
||||
struct RequestState<'r> {
|
||||
|
@ -286,30 +289,19 @@ impl<'r> Request<'r> {
|
|||
|
||||
#[inline]
|
||||
pub fn session(&self) -> Session {
|
||||
let key = self.preset().config.secret_key();
|
||||
match self.extra.session.try_borrow_mut() {
|
||||
Ok(jar) => Session::new(jar, self.preset().key),
|
||||
Ok(jar) => Session::new(jar, key),
|
||||
Err(_) => {
|
||||
error_!("Multiple `Session` instances are active at once.");
|
||||
info_!("An instance of `Session` must be dropped before another \
|
||||
can be retrieved.");
|
||||
warn_!("The retrieved `Session` instance will be empty.");
|
||||
Session::empty(self.preset().key)
|
||||
Session::empty(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace all of the cookies in `self` with those in `jar`.
|
||||
#[inline]
|
||||
pub(crate) fn set_cookies(&mut self, jar: CookieJar) {
|
||||
self.extra.cookies = RefCell::new(jar);
|
||||
}
|
||||
|
||||
/// Replace all of the session cookie in `self` with those in `jar`.
|
||||
#[inline]
|
||||
pub(crate) fn set_session(&mut self, jar: CookieJar) {
|
||||
self.extra.session = RefCell::new(jar);
|
||||
}
|
||||
|
||||
/// Returns the Content-Type header of `self`. If the header is not present,
|
||||
/// returns `None`. The Content-Type header is cached after the first call
|
||||
/// to this function. As a result, subsequent calls will always return the
|
||||
|
@ -352,7 +344,6 @@ impl<'r> Request<'r> {
|
|||
self.accept().and_then(|accept| accept.first()).map(|wmt| wmt.media_type())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn format(&self) -> Option<&MediaType> {
|
||||
static ANY: MediaType = MediaType::Any;
|
||||
if self.method.supports_payload() {
|
||||
|
@ -386,7 +377,7 @@ impl<'r> Request<'r> {
|
|||
///
|
||||
/// # #[allow(dead_code)]
|
||||
/// fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> {
|
||||
/// Outcome::of(req.get_param::<String>(0).unwrap_or("unnamed".into()))
|
||||
/// Outcome::from(req, req.get_param::<String>(0).unwrap_or("unnamed".into()))
|
||||
/// }
|
||||
/// ```
|
||||
pub fn get_param<'a, T: FromParam<'a>>(&'a self, n: usize) -> Result<T, Error> {
|
||||
|
@ -394,15 +385,6 @@ impl<'r> Request<'r> {
|
|||
T::from_param(param).map_err(|_| Error::BadParse)
|
||||
}
|
||||
|
||||
/// Set `self`'s parameters given that the route used to reach this request
|
||||
/// was `route`. This should only be used internally by `Rocket` as improper
|
||||
/// use may result in out of bounds indexing.
|
||||
/// TODO: Figure out the mount path from here.
|
||||
#[inline]
|
||||
pub(crate) fn set_params(&self, route: &Route) {
|
||||
*self.extra.params.borrow_mut() = route.get_param_indexes(self.uri());
|
||||
}
|
||||
|
||||
/// Get the `n`th path parameter as a string, if it exists. This is used by
|
||||
/// codegen.
|
||||
#[doc(hidden)]
|
||||
|
@ -468,10 +450,9 @@ impl<'r> Request<'r> {
|
|||
Some(Segments(&path[i..j]))
|
||||
}
|
||||
|
||||
/// Get the managed state container, if it exists. For internal use only!
|
||||
#[inline(always)]
|
||||
pub fn get_state<T: Send + Sync + 'static>(&self) -> Option<&'r T> {
|
||||
self.preset().managed_state.try_get()
|
||||
/// Get the limits.
|
||||
pub fn limits(&self) -> &'r Limits {
|
||||
&self.preset().config.limits
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -485,10 +466,43 @@ impl<'r> Request<'r> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set `self`'s parameters given that the route used to reach this request
|
||||
/// was `route`. This should only be used internally by `Rocket` as improper
|
||||
/// use may result in out of bounds indexing.
|
||||
/// TODO: Figure out the mount path from here.
|
||||
#[inline]
|
||||
pub(crate) fn set_params(&self, route: &Route) {
|
||||
*self.extra.params.borrow_mut() = route.get_param_indexes(self.uri());
|
||||
}
|
||||
|
||||
/// Replace all of the cookies in `self` with those in `jar`.
|
||||
#[inline]
|
||||
pub(crate) fn set_cookies(&mut self, jar: CookieJar) {
|
||||
self.extra.cookies = RefCell::new(jar);
|
||||
}
|
||||
|
||||
/// Replace all of the session cookie in `self` with those in `jar`.
|
||||
#[inline]
|
||||
pub(crate) fn set_session(&mut self, jar: CookieJar) {
|
||||
self.extra.session = RefCell::new(jar);
|
||||
}
|
||||
|
||||
/// Try to derive some guarded value from `self`.
|
||||
#[inline(always)]
|
||||
pub fn guard<'a, T: FromRequest<'a, 'r>>(&'a self) -> Outcome<T, T::Error> {
|
||||
T::from_request(self)
|
||||
}
|
||||
|
||||
/// Get the managed state T, if it exists. For internal use only!
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_state<T: Send + Sync + 'static>(&self) -> Option<&'r T> {
|
||||
self.preset().state.try_get()
|
||||
}
|
||||
|
||||
/// Set the precomputed state. For internal use only!
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_preset_state(&mut self, key: &'r Key, state: &'r Container) {
|
||||
self.extra.preset = Some(PresetState { key, managed_state: state });
|
||||
pub(crate) fn set_preset(&mut self, config: &'r Config, state: &'r Container) {
|
||||
self.extra.preset = Some(PresetState { config, state });
|
||||
}
|
||||
|
||||
/// Convert from Hyper types into a Rocket Request.
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
//! let response = content::HTML("<h1>Hello, world!</h1>");
|
||||
//! ```
|
||||
|
||||
use request::Request;
|
||||
use response::{Response, Responder};
|
||||
use http::{Status, ContentType};
|
||||
|
||||
|
@ -47,9 +48,9 @@ pub struct Content<R>(pub ContentType, pub R);
|
|||
/// delegates the remainder of the response to the wrapped responder.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for Content<R> {
|
||||
#[inline(always)]
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
Response::build()
|
||||
.merge(self.1.respond()?)
|
||||
.merge(self.1.respond_to(req)?)
|
||||
.header(self.0)
|
||||
.ok()
|
||||
}
|
||||
|
@ -71,8 +72,8 @@ macro_rules! ctrs {
|
|||
/// Sets the Content-Type of the response then delegates the
|
||||
/// remainder of the response to the wrapped responder.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for $name<R> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
Content(ContentType::$name, self.0).respond()
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
Content(ContentType::$name, self.0).respond_to(req)
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use request::Request;
|
||||
use response::{Response, Responder};
|
||||
use http::Status;
|
||||
|
||||
|
@ -6,8 +7,8 @@ use http::Status;
|
|||
#[derive(Debug)]
|
||||
pub struct Failure(pub Status);
|
||||
|
||||
impl<'r> Responder<'r> for Failure {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
impl Responder<'static> for Failure {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
Err(self.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -181,10 +181,10 @@ impl<'r, R: Responder<'r>> Flash<R> {
|
|||
/// response handling to the wrapped responder. As a result, the `Outcome` of
|
||||
/// the response is the `Outcome` of the wrapped `Responder`.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for Flash<R> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
trace_!("Flash: setting message: {}:{}", self.name, self.message);
|
||||
let cookie = self.cookie();
|
||||
Response::build_from(self.responder.respond()?)
|
||||
Response::build_from(self.responder.respond_to(req)?)
|
||||
.header_adjoin(&cookie)
|
||||
.ok()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::io;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use request::Request;
|
||||
use response::{Response, Responder};
|
||||
use http::{Status, ContentType};
|
||||
|
||||
|
@ -78,8 +79,8 @@ impl NamedFile {
|
|||
/// [ContentType::from_extension](/rocket/http/struct.ContentType.html#method.from_extension)
|
||||
/// for more information. If you would like to stream a file with a different
|
||||
/// Content-Type than that implied by its extension, use a `File` directly.
|
||||
impl<'r> Responder<'r> for NamedFile {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
impl Responder<'static> for NamedFile {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
let mut response = Response::new();
|
||||
if let Some(ext) = self.path().extension() {
|
||||
// TODO: Use Cow for lowercase.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use request::Request;
|
||||
use response::{Response, Responder};
|
||||
use http::hyper::header;
|
||||
use http::Status;
|
||||
|
@ -104,7 +105,7 @@ impl Redirect {
|
|||
/// the `Location` header field. The body of the response is empty. This
|
||||
/// responder does not fail.
|
||||
impl Responder<'static> for Redirect {
|
||||
fn respond(self) -> Result<Response<'static>, Status> {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
Response::build()
|
||||
.status(self.0)
|
||||
.header(header::Location(self.1))
|
||||
|
|
|
@ -3,7 +3,8 @@ use std::io::Cursor;
|
|||
use std::fmt;
|
||||
|
||||
use http::{Status, ContentType};
|
||||
use response::{Response, Stream};
|
||||
use response::Response;
|
||||
use request::Request;
|
||||
|
||||
/// Trait implemented by types that generate responses for clients.
|
||||
///
|
||||
|
@ -49,12 +50,14 @@ use response::{Response, Stream};
|
|||
///
|
||||
/// * **File**
|
||||
///
|
||||
/// Streams the `File` to the client. This is essentially an alias to
|
||||
/// `Stream::from(file)`.
|
||||
/// Responds with a sized body containing the data in the `File`. No
|
||||
/// `Content-Type` is set. To automatically have a `Content-Type` set based
|
||||
/// on the file's extension, use
|
||||
/// [`NamedFile`](/rocket/response/struct.NamedFile.html).
|
||||
///
|
||||
/// * **()**
|
||||
///
|
||||
/// Responds with an empty body. No Content-Type is set.
|
||||
/// Responds with an empty body. No `Content-Type` is set.
|
||||
///
|
||||
/// * **Option<T>**
|
||||
///
|
||||
|
@ -95,6 +98,16 @@ use response::{Response, Stream};
|
|||
/// or `ResponseBuilder` struct. Ensure that you document the merging or joining
|
||||
/// behavior appropriately.
|
||||
///
|
||||
/// ## Inspecting Requests
|
||||
///
|
||||
/// A `Responder` has access to the request it is responding to. Even so, you
|
||||
/// should avoid using the `Request` value as much as possible. This is because
|
||||
/// using the `Request` object makes your responder _inpure_, and so the use of
|
||||
/// the type as a `Responder` has less intrinsic meaning associated with it. If
|
||||
/// the `Responder` were pure, however, it always respond in the same manner,
|
||||
/// regardless of the incoming request. Thus, knowing the type is sufficient to
|
||||
/// fully determine its functionality.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Say that you have a custom type, `Person`:
|
||||
|
@ -133,11 +146,12 @@ use response::{Response, Stream};
|
|||
/// #
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// use rocket::request::Request;
|
||||
/// use rocket::response::{self, Response, Responder};
|
||||
/// use rocket::http::ContentType;
|
||||
///
|
||||
/// impl<'r> Responder<'r> for Person {
|
||||
/// fn respond(self) -> response::Result<'r> {
|
||||
/// impl Responder<'static> for Person {
|
||||
/// fn respond_to(self, _: &Request) -> response::Result<'static> {
|
||||
/// Response::build()
|
||||
/// .sized_body(Cursor::new(format!("{}:{}", self.name, self.age)))
|
||||
/// .raw_header("X-Person-Name", self.name)
|
||||
|
@ -155,32 +169,21 @@ pub trait Responder<'r> {
|
|||
/// Returns `Ok` if a `Response` could be generated successfully. Otherwise,
|
||||
/// returns an `Err` with a failing `Status`.
|
||||
///
|
||||
/// The `request` parameter is the `Request` that this `Responder` is
|
||||
/// responding to.
|
||||
///
|
||||
/// When using Rocket's code generation, if an `Ok(Response)` is returned,
|
||||
/// the response will be written out to the client. If an `Err(Status)` is
|
||||
/// returned, the error catcher for the given status is retrieved and called
|
||||
/// to generate a final error response, which is then written out to the
|
||||
/// client.
|
||||
fn respond(self) -> Result<Response<'r>, Status>;
|
||||
fn respond_to(self, request: &Request) -> Result<Response<'r>, Status>;
|
||||
}
|
||||
|
||||
/// Returns a response with Content-Type `text/plain` and a fixed-size body
|
||||
/// containing the string `self`. Always returns `Ok`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::Responder;
|
||||
/// use rocket::http::ContentType;
|
||||
///
|
||||
/// let mut response = "Hello".respond().unwrap();
|
||||
/// assert_eq!(response.body_string(), Some("Hello".into()));
|
||||
///
|
||||
/// let content_type: Vec<_> = response.headers().get("Content-Type").collect();
|
||||
/// assert_eq!(content_type.len(), 1);
|
||||
/// assert_eq!(content_type[0], ContentType::Plain.to_string());
|
||||
/// ```
|
||||
impl<'r> Responder<'r> for &'r str {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
|
||||
Response::build()
|
||||
.header(ContentType::Plain)
|
||||
.sized_body(Cursor::new(self))
|
||||
|
@ -191,7 +194,7 @@ impl<'r> Responder<'r> for &'r str {
|
|||
/// Returns a response with Content-Type `text/plain` and a fixed-size body
|
||||
/// containing the string `self`. Always returns `Ok`.
|
||||
impl Responder<'static> for String {
|
||||
fn respond(self) -> Result<Response<'static>, Status> {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
Response::build()
|
||||
.header(ContentType::Plain)
|
||||
.sized_body(Cursor::new(self))
|
||||
|
@ -199,16 +202,16 @@ impl Responder<'static> for String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Aliases Stream<File>: `Stream::from(self)`.
|
||||
/// Returns a response with a sized body for the file. Always returns `Ok`.
|
||||
impl Responder<'static> for File {
|
||||
fn respond(self) -> Result<Response<'static>, Status> {
|
||||
Stream::from(self).respond()
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
Response::build().sized_body(self).ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an empty, default `Response`. Always returns `Ok`.
|
||||
impl Responder<'static> for () {
|
||||
fn respond(self) -> Result<Response<'static>, Status> {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
Ok(Response::new())
|
||||
}
|
||||
}
|
||||
|
@ -216,11 +219,11 @@ impl Responder<'static> for () {
|
|||
/// If `self` is `Some`, responds with the wrapped `Responder`. Otherwise prints
|
||||
/// a warning message and returns an `Err` of `Status::NotFound`.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for Option<R> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
self.map_or_else(|| {
|
||||
warn_!("Response was `None`.");
|
||||
Err(Status::NotFound)
|
||||
}, |r| r.respond())
|
||||
}, |r| r.respond_to(req))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,8 +231,8 @@ impl<'r, R: Responder<'r>> Responder<'r> for Option<R> {
|
|||
/// an error message with the `Err` value returns an `Err` of
|
||||
/// `Status::InternalServerError`.
|
||||
impl<'r, R: Responder<'r>, E: fmt::Debug> Responder<'r> for Result<R, E> {
|
||||
default fn respond(self) -> Result<Response<'r>, Status> {
|
||||
self.map(|r| r.respond()).unwrap_or_else(|e| {
|
||||
default fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
self.map(|r| r.respond_to(req)).unwrap_or_else(|e| {
|
||||
error_!("Response was `Err`: {:?}.", e);
|
||||
Err(Status::InternalServerError)
|
||||
})
|
||||
|
@ -239,10 +242,10 @@ impl<'r, R: Responder<'r>, E: fmt::Debug> Responder<'r> for Result<R, E> {
|
|||
/// Responds with the wrapped `Responder` in `self`, whether it is `Ok` or
|
||||
/// `Err`.
|
||||
impl<'r, R: Responder<'r>, E: Responder<'r> + fmt::Debug> Responder<'r> for Result<R, E> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
match self {
|
||||
Ok(responder) => responder.respond(),
|
||||
Err(responder) => responder.respond(),
|
||||
Ok(responder) => responder.respond_to(req),
|
||||
Err(responder) => responder.respond_to(req),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1129,9 +1129,11 @@ impl<'r> fmt::Debug for Response<'r> {
|
|||
}
|
||||
}
|
||||
|
||||
use request::Request;
|
||||
|
||||
impl<'r> Responder<'r> for Response<'r> {
|
||||
/// This is the identity implementation. It simply returns `Ok(self)`.
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
||||
use request::Request;
|
||||
use response::{Responder, Response};
|
||||
use http::hyper::header;
|
||||
use http::Status;
|
||||
|
@ -40,10 +41,10 @@ pub struct Created<R>(pub String, pub Option<R>);
|
|||
/// information about the created resource. If no responder is provided, the
|
||||
/// response body will be empty.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for Created<R> {
|
||||
default fn respond(self) -> Result<Response<'r>, Status> {
|
||||
default fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
let mut build = Response::build();
|
||||
if let Some(responder) = self.1 {
|
||||
build.merge(responder.respond()?);
|
||||
build.merge(responder.respond_to(req)?);
|
||||
}
|
||||
|
||||
build.status(Status::Created).header(header::Location(self.0)).ok()
|
||||
|
@ -55,14 +56,14 @@ impl<'r, R: Responder<'r>> Responder<'r> for Created<R> {
|
|||
/// a `Responder` is provided that implements `Hash`. The `ETag` header is set
|
||||
/// to a hash value of the responder.
|
||||
impl<'r, R: Responder<'r> + Hash> Responder<'r> for Created<R> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
let mut hasher = DefaultHasher::default();
|
||||
let mut build = Response::build();
|
||||
if let Some(responder) = self.1 {
|
||||
responder.hash(&mut hasher);
|
||||
let hash = hasher.finish().to_string();
|
||||
|
||||
build.merge(responder.respond()?);
|
||||
build.merge(responder.respond_to(req)?);
|
||||
build.header(header::ETag(header::EntityTag::strong(hash)));
|
||||
}
|
||||
|
||||
|
@ -100,10 +101,10 @@ pub struct Accepted<R>(pub Option<R>);
|
|||
/// Sets the status code of the response to 202 Accepted. If the responder is
|
||||
/// `Some`, it is used to finalize the response.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for Accepted<R> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
let mut build = Response::build();
|
||||
if let Some(responder) = self.0 {
|
||||
build.merge(responder.respond()?);
|
||||
build.merge(responder.respond_to(req)?);
|
||||
}
|
||||
|
||||
build.status(Status::Accepted).ok()
|
||||
|
@ -127,7 +128,7 @@ pub struct NoContent;
|
|||
/// Sets the status code of the response to 204 No Content. The body of the
|
||||
/// response will be empty.
|
||||
impl<'r> Responder<'r> for NoContent {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
|
||||
Response::build().status(Status::NoContent).ok()
|
||||
}
|
||||
}
|
||||
|
@ -148,8 +149,8 @@ pub struct Reset;
|
|||
|
||||
/// Sets the status code of the response to 205 Reset Content. The body of the
|
||||
/// response will be empty.
|
||||
impl<'r> Responder<'r> for Reset {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
impl Responder<'static> for Reset {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
Response::build().status(Status::ResetContent).ok()
|
||||
}
|
||||
}
|
||||
|
@ -171,8 +172,8 @@ pub struct Custom<R>(pub Status, pub R);
|
|||
/// Sets the status code of the response and then delegates the remainder of the
|
||||
/// response to the wrapped responder.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for Custom<R> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
Response::build_from(self.1.respond()?)
|
||||
fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
|
||||
Response::build_from(self.1.respond_to(req)?)
|
||||
.status(self.0)
|
||||
.ok()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::io::Read;
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
use request::Request;
|
||||
use response::{Response, Responder, DEFAULT_CHUNK_SIZE};
|
||||
use http::Status;
|
||||
|
||||
|
@ -68,7 +69,7 @@ impl<T: Read> From<T> for Stream<T> {
|
|||
/// response is abandoned, and the response ends abruptly. An error is printed
|
||||
/// to the console with an indication of what went wrong.
|
||||
impl<'r, T: Read + 'r> Responder<'r> for Stream<T> {
|
||||
fn respond(self) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
|
||||
Response::build().chunked_body(self.0, self.1).ok()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ use http::uri::URI;
|
|||
/// The main `Rocket` type: used to mount routes and catchers and launch the
|
||||
/// application.
|
||||
pub struct Rocket {
|
||||
config: &'static Config,
|
||||
config: Config,
|
||||
router: Router,
|
||||
default_catchers: HashMap<u16, Catcher>,
|
||||
catchers: HashMap<u16, Catcher>,
|
||||
|
@ -96,8 +96,8 @@ macro_rules! serve {
|
|||
#[cfg(feature = "tls")]
|
||||
macro_rules! serve {
|
||||
($rocket:expr, $addr:expr, |$server:ident, $proto:ident| $continue:expr) => ({
|
||||
if let Some(ref tls) = $rocket.config.tls {
|
||||
let tls = TlsServer::new(tls.certs.clone(), tls.key.clone());
|
||||
if let Some(tls) = $rocket.config.tls.clone() {
|
||||
let tls = TlsServer::new(tls.certs, tls.key);
|
||||
let ($proto, $server) = ("https://", hyper::Server::https($addr, tls));
|
||||
$continue
|
||||
} else {
|
||||
|
@ -218,7 +218,7 @@ impl Rocket {
|
|||
info!("{}:", request);
|
||||
|
||||
// Inform the request about all of the precomputed state.
|
||||
request.set_preset_state(&self.config.secret_key(), &self.state);
|
||||
request.set_preset(&self.config, &self.state);
|
||||
|
||||
// Do a bit of preprocessing before routing; run the attached fairings.
|
||||
self.preprocess_request(request, &data);
|
||||
|
@ -346,10 +346,10 @@ impl Rocket {
|
|||
/// rocket::ignite()
|
||||
/// # };
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn ignite() -> Rocket {
|
||||
// Note: init() will exit the process under config errors.
|
||||
let (config, initted) = config::init();
|
||||
Rocket::configured(config, initted)
|
||||
Rocket::configured(config::init(), true)
|
||||
}
|
||||
|
||||
/// Creates a new `Rocket` application using the supplied custom
|
||||
|
@ -377,14 +377,15 @@ impl Rocket {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn custom(config: Config, log: bool) -> Rocket {
|
||||
let (config, initted) = config::custom_init(config);
|
||||
Rocket::configured(config, log && initted)
|
||||
Rocket::configured(config, log)
|
||||
}
|
||||
|
||||
fn configured(config: &'static Config, log: bool) -> Rocket {
|
||||
#[inline]
|
||||
fn configured(config: Config, log: bool) -> Rocket {
|
||||
if log {
|
||||
logger::init(config.log_level);
|
||||
logger::try_init(config.log_level, false);
|
||||
}
|
||||
|
||||
info!("🔧 Configured for {}.", config.environment);
|
||||
|
@ -426,17 +427,17 @@ impl Rocket {
|
|||
/// path. Mounting a route with path `path` at path `base` makes the route
|
||||
/// available at `base/path`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The `base` mount point must be a static path. That is, the mount point
|
||||
/// must _not_ contain dynamic path parameters: `<param>`.
|
||||
///
|
||||
/// # 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.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The `base` mount point must be a static path. That is, the mount point
|
||||
/// must _not_ contain dynamic path parameters: `<param>`.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(plugin)]
|
||||
/// # #![plugin(rocket_codegen)]
|
||||
|
@ -464,8 +465,8 @@ impl Rocket {
|
|||
/// use rocket::handler::Outcome;
|
||||
/// use rocket::http::Method::*;
|
||||
///
|
||||
/// fn hi(_: &Request, _: Data) -> Outcome<'static> {
|
||||
/// Outcome::of("Hello!")
|
||||
/// fn hi(req: &Request, _: Data) -> Outcome<'static> {
|
||||
/// Outcome::from(req, "Hello!")
|
||||
/// }
|
||||
///
|
||||
/// # if false { // We don't actually want to launch the server in an example.
|
||||
|
@ -473,6 +474,7 @@ impl Rocket {
|
|||
/// # .launch();
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn mount(mut self, base: &str, routes: Vec<Route>) -> Self {
|
||||
info!("🛰 {} '{}':", Magenta.paint("Mounting"), base);
|
||||
|
||||
|
@ -522,6 +524,7 @@ impl Rocket {
|
|||
/// # }
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn catch(mut self, catchers: Vec<Catcher>) -> Self {
|
||||
info!("👾 {}:", Magenta.paint("Catchers"));
|
||||
for c in catchers {
|
||||
|
@ -577,6 +580,7 @@ impl Rocket {
|
|||
/// # }
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
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!");
|
||||
|
@ -648,7 +652,7 @@ impl Rocket {
|
|||
/// rocket::ignite().launch();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn launch(self) -> LaunchError {
|
||||
pub fn launch(mut self) -> LaunchError {
|
||||
if let Some(error) = self.prelaunch_check() {
|
||||
return error;
|
||||
}
|
||||
|
@ -662,19 +666,20 @@ impl Rocket {
|
|||
Err(e) => return LaunchError::from(e)
|
||||
};
|
||||
|
||||
// Determine the port we actually binded to.
|
||||
let (addr, port) = match server.local_addr() {
|
||||
Ok(server_addr) => (&self.config.address, server_addr.port()),
|
||||
// Determine the address and port we actually binded to.
|
||||
match server.local_addr() {
|
||||
Ok(server_addr) => self.config.port = server_addr.port(),
|
||||
Err(e) => return LaunchError::from(e)
|
||||
};
|
||||
}
|
||||
|
||||
// Run the launch fairings.
|
||||
self.fairings.handle_launch(&self);
|
||||
|
||||
let full_addr = format!("{}:{}", self.config.address, self.config.port);
|
||||
launch_info!("🚀 {} {}{}",
|
||||
White.paint("Rocket has launched from"),
|
||||
White.bold().paint(proto),
|
||||
White.bold().paint(&format!("{}:{}", addr, port)));
|
||||
White.bold().paint(&full_addr));
|
||||
|
||||
let threads = self.config.workers as usize;
|
||||
if let Err(e) = server.handle_threads(self, threads) {
|
||||
|
@ -691,9 +696,9 @@ impl Rocket {
|
|||
self.router.routes()
|
||||
}
|
||||
|
||||
/// Retrieve the configuration.
|
||||
/// Retrieve the active configuration.
|
||||
#[inline(always)]
|
||||
pub fn config(&self) -> &Config {
|
||||
self.config
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,8 +141,8 @@ mod tests {
|
|||
|
||||
type SimpleRoute = (Method, &'static str);
|
||||
|
||||
fn dummy_handler(_req: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::of("hi")
|
||||
fn dummy_handler(req: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::from(req, "hi")
|
||||
}
|
||||
|
||||
fn m_collide(a: SimpleRoute, b: SimpleRoute) -> bool {
|
||||
|
|
|
@ -77,8 +77,8 @@ mod test {
|
|||
use data::Data;
|
||||
use handler::Outcome;
|
||||
|
||||
fn dummy_handler(_req: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::of("hi")
|
||||
fn dummy_handler(req: &Request, _: Data) -> Outcome<'static> {
|
||||
Outcome::from(req, "hi")
|
||||
}
|
||||
|
||||
fn router_with_routes(routes: &[&'static str]) -> Router {
|
||||
|
|
|
@ -15,7 +15,7 @@ fn index(form: Form<Simple>) -> String {
|
|||
form.into_inner().value
|
||||
}
|
||||
|
||||
mod tests {
|
||||
mod limits_tests {
|
||||
use rocket;
|
||||
use rocket::config::{Environment, Config, Limits};
|
||||
use rocket::testing::MockRequest;
|
||||
|
@ -30,44 +30,38 @@ mod tests {
|
|||
rocket::custom(config, true).mount("/", routes![super::index])
|
||||
}
|
||||
|
||||
// FIXME: Config is global (it's the only global thing). Each of these tests
|
||||
// will run in different threads in the same process, so the config used by
|
||||
// all of the tests will be indentical: whichever of these gets executed
|
||||
// first. As such, only one test will pass; the rest will fail. Make config
|
||||
// _not_ global so we can actually do these tests.
|
||||
#[test]
|
||||
fn large_enough() {
|
||||
let rocket = rocket_with_forms_limit(128);
|
||||
let mut req = MockRequest::new(Post, "/")
|
||||
.body("value=Hello+world")
|
||||
.header(ContentType::Form);
|
||||
|
||||
// #[test]
|
||||
// fn large_enough() {
|
||||
// let rocket = rocket_with_forms_limit(128);
|
||||
// let mut req = MockRequest::new(Post, "/")
|
||||
// .body("value=Hello+world")
|
||||
// .header(ContentType::Form);
|
||||
let mut response = req.dispatch_with(&rocket);
|
||||
assert_eq!(response.body_string(), Some("Hello world".into()));
|
||||
}
|
||||
|
||||
// let mut response = req.dispatch_with(&rocket);
|
||||
// assert_eq!(response.body_string(), Some("Hello world".into()));
|
||||
// }
|
||||
#[test]
|
||||
fn just_large_enough() {
|
||||
let rocket = rocket_with_forms_limit(17);
|
||||
let mut req = MockRequest::new(Post, "/")
|
||||
.body("value=Hello+world")
|
||||
.header(ContentType::Form);
|
||||
|
||||
// #[test]
|
||||
// fn just_large_enough() {
|
||||
// let rocket = rocket_with_forms_limit(17);
|
||||
// let mut req = MockRequest::new(Post, "/")
|
||||
// .body("value=Hello+world")
|
||||
// .header(ContentType::Form);
|
||||
let mut response = req.dispatch_with(&rocket);
|
||||
assert_eq!(response.body_string(), Some("Hello world".into()));
|
||||
}
|
||||
|
||||
// let mut response = req.dispatch_with(&rocket);
|
||||
// assert_eq!(response.body_string(), Some("Hello world".into()));
|
||||
// }
|
||||
#[test]
|
||||
fn much_too_small() {
|
||||
let rocket = rocket_with_forms_limit(4);
|
||||
let mut req = MockRequest::new(Post, "/")
|
||||
.body("value=Hello+world")
|
||||
.header(ContentType::Form);
|
||||
|
||||
// #[test]
|
||||
// fn much_too_small() {
|
||||
// let rocket = rocket_with_forms_limit(4);
|
||||
// let mut req = MockRequest::new(Post, "/")
|
||||
// .body("value=Hello+world")
|
||||
// .header(ContentType::Form);
|
||||
|
||||
// let response = req.dispatch_with(&rocket);
|
||||
// assert_eq!(response.status(), Status::BadRequest);
|
||||
// }
|
||||
let response = req.dispatch_with(&rocket);
|
||||
assert_eq!(response.status(), Status::BadRequest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contracted() {
|
||||
|
|
|
@ -128,7 +128,7 @@ fn rocket_route_fn_hello<'_b>(_req: &'_b ::rocket::Request,
|
|||
_data: ::rocket::Data)
|
||||
-> ::rocket::handler::Outcome<'_b> {
|
||||
let responder = hello();
|
||||
::rocket::handler::Outcome::of(responder)
|
||||
::rocket::handler::Outcome::from(_req, responder)
|
||||
}
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in New Issue