diff --git a/README.md b/README.md index 24546abe..f199fd94 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ -# Rocket [![Build Status](https://travis-ci.org/SergioBenitez/Rocket.svg?branch=master)](https://travis-ci.org/SergioBenitez/Rocket) [![Rocket Homepage](https://img.shields.io/badge/web-rocket.rs-red.svg?style=flat&label=https&colorB=d33847)](https://rocket.rs) [![Current Crates.io Version](https://img.shields.io/crates/v/rocket.svg)](https://crates.io/crates/rocket) +# Rocket + +[![Build Status](https://travis-ci.org/SergioBenitez/Rocket.svg?branch=master)](https://travis-ci.org/SergioBenitez/Rocket) +[![Rocket Homepage](https://img.shields.io/badge/web-rocket.rs-red.svg?style=flat&label=https&colorB=d33847)](https://rocket.rs) +[![Current Crates.io Version](https://img.shields.io/crates/v/rocket.svg)](https://crates.io/crates/rocket) +[![Chat on Matrix](https://img.shields.io/badge/style-matrix-blue.svg?style=flat&label=chat)](https://riot.im/app/#/room/#mozilla_#rocket:matrix.org) +[![IRC: #rocket on irc.mozilla.org](https://img.shields.io/badge/style-%23rocket-blue.svg?style=flat&label=mozilla)](https://kiwiirc.com/client/irc.mozilla.org/#rocket) Rocket is web framework for Rust (nightly) with a focus on ease-of-use, expressability, and speed. Here's an example of a complete Rocket application: @@ -40,12 +46,16 @@ Rocket is extensively documented: [Guide]: https://rocket.rs/guide [API Documentation]: https://api.rocket.rs/rocket -The official community support channel is via the [Mozilla IRC -Server](https://wiki.mozilla.org/IRC) at `irc.mozilla.org` in channel `#rocket`. -If you're not familiar with IRC, see Mozilla's [Getting Started with +The official community support channels are the `#rocket` IRC channel on the +[Mozilla IRC Server](https://wiki.mozilla.org/IRC) at `irc.mozilla.org` and the +bridged [Rocket room on +Matrix](https://riot.im/app/#/room/#mozilla_#rocket:matrix.org). If you're not +familiar with IRC, we recommend chatting through [Matrix via +Riot](https://riot.im/app/#/room/#mozilla_#rocket:matrix.org) or via the [Kiwi +web IRC client](https://kiwiirc.com/client/irc.mozilla.org/#rocket). You can +learn more about IRC via Mozilla's [Getting Started with IRC](https://developer.mozilla.org/en-US/docs/Mozilla/QA/Getting_Started_with_IRC) -guide. You can find general Rust support in `#rust` or `#rust-beginners` on the -same network. +guide. ## Building @@ -137,8 +147,9 @@ Apache License, Version 2.0, without any additional terms or conditions. Rocket is designed to be performant. At this time, its performance is [bottlenecked by the Hyper HTTP library](https://github.com/SergioBenitez/Rocket/issues/17). Even so, Rocket -currently performs _better_ than the latest version of Hyper on a simple "Hello, -world!" benchmark: +currently performs _significantly better_ than the latest version of +asynchronous Hyper on a simple "Hello, world!" benchmark. Rocket also performs +_significantly better_ than the Iron web framework: **Machine Specs:** @@ -147,32 +158,45 @@ world!" benchmark: * **Processor:** Intel Xeon X5675 @ 3.07GHz * **Operating System:** Mac OS X v10.11.6 -**Hyper v0.10.0-a.0** (46 LOC) results (best of 3, +/- 2000 req/s, +/- 10us latency): +**Rocket v0.2-rc** (8 LOC) results (best of 3, +/- 2000 req/s, +/- 5us latency): Running 10s test @ http://localhost:80 - 2 threads and 10 connections + 1 threads and 18 connections Thread Stats Avg Stdev Max +/- Stdev - Latency 175.12us 40.38us 429.00us 70.79% - Req/Sec 28.00k 2.41k 36.79k 72.28% - 562692 requests in 10.10s, 81.57MB read - Requests/sec: 55715.98 - Transfer/sec: 8.08MB + Latency 153.01us 42.25us 449.00us 75.54% + Req/Sec 75.58k 11.75k 90.22k 54.46% + 758044 requests in 10.10s, 105.55MB read + Requests/sec: 75051.28 + Transfer/sec: 10.45MB -**Rocket v0.1.0** (8 LOC) results (best of 3, +/- 1000 req/s, +/- 5us latency): +**Hyper v0.10.0-a.0 (1/12/2016)** (46 LOC) results (best of 3, +/- 5000 req/s, +/- 30us latency): Running 10s test @ http://localhost:80 - 2 threads and 10 connections + 1 threads and 18 connections Thread Stats Avg Stdev Max +/- Stdev - Latency 161.33us 37.40us 2.08ms 75.89% - Req/Sec 30.10k 1.13k 33.28k 72.77% - 604782 requests in 10.10s, 84.21MB read - Requests/sec: 59883.30 - Transfer/sec: 8.34MB + Latency 287.81us 77.09us 606.00us 70.47% + Req/Sec 59.94k 6.01k 79.72k 71.00% + 596231 requests in 10.00s, 83.02MB read + Requests/sec: 59621.32 + Transfer/sec: 8.30MB + +**Iron v0.5.0** (11 LOC) results (best of 3, +/- 3000 req/s, +/- 500us latency): + + Running 10s test @ http://localhost:80 + 1 threads and 18 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 512.36us 5.57ms 149.99ms 99.60% + Req/Sec 58.25k 11.61k 70.47k 46.00% + 579227 requests in 10.00s, 80.65MB read + Requests/sec: 57920.73 + Transfer/sec: 8.06MB **Summary:** - * Rocket throughput higher by 7.5% (higher is better). - * Rocket latency lower by 7.8% (lower is better). + * Rocket throughput higher by 25.9% (higher is better) compared to Hyper. + * Rocket throughput higher by 29.6% (higher is better) compared to Iron. + * Rocket latency lower by 46.8% (lower is better) compared to Hyper. + * Rocket latency lower by 70.1% (lower is better) compared to Iron. ### Future Improvements diff --git a/contrib/Cargo.toml b/contrib/Cargo.toml index d10e3608..1ad7857a 100644 --- a/contrib/Cargo.toml +++ b/contrib/Cargo.toml @@ -32,7 +32,8 @@ serde = { version = "^0.8", optional = true } serde_json = { version = "^0.8", optional = true } # Templating dependencies only. -handlebars = { version = "^0.23", optional = true, features = ["serde_type"] } +handlebars = { version = "^0.24", optional = true, features = ["serde_type"] } glob = { version = "^0.2", optional = true } lazy_static = { version = "^0.2", optional = true } -tera = { version = "^0.6", optional = true } +# tera = { version = "^0.6", optional = true } +tera = { git = "https://github.com/SergioBenitez/tera", optional = true } diff --git a/contrib/src/lib.rs b/contrib/src/lib.rs index f1a6381e..334475b9 100644 --- a/contrib/src/lib.rs +++ b/contrib/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(drop_types_in_const)] + //! This crate contains officially sanctioned contributor libraries that provide //! functionality commonly used by Rocket applications. //! diff --git a/contrib/src/templates/handlebars_templates.rs b/contrib/src/templates/handlebars_templates.rs index b750e7e1..85dcdec8 100644 --- a/contrib/src/templates/handlebars_templates.rs +++ b/contrib/src/templates/handlebars_templates.rs @@ -1,31 +1,53 @@ extern crate handlebars; -use std::sync::RwLock; - use super::serde::Serialize; use super::TemplateInfo; use self::handlebars::Handlebars; -lazy_static! { - static ref HANDLEBARS: RwLock = RwLock::new(Handlebars::new()); -} +static mut HANDLEBARS: Option = None; pub const EXT: &'static str = "hbs"; -pub fn render(name: &str, info: &TemplateInfo, context: &T) -> Option - where T: Serialize -{ - // FIXME: Expose a callback to register each template at launch => no lock. - if HANDLEBARS.read().unwrap().get_template(name).is_none() { - let p = &info.full_path; - if let Err(e) = HANDLEBARS.write().unwrap().register_template_file(name, p) { +// 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); - return None; + success = false; } } - match HANDLEBARS.read().unwrap().render(name, context) { + HANDLEBARS = Some(hb); + success +} + +pub fn render(name: &str, _info: &TemplateInfo, context: &T) -> Option + where T: Serialize +{ + let hb = match unsafe { HANDLEBARS.as_ref() } { + Some(hb) => hb, + None => { + error_!("Internal error: `render` called before handlebars init."); + 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); diff --git a/contrib/src/templates/macros.rs b/contrib/src/templates/macros.rs index 2527af98..d4649d73 100644 --- a/contrib/src/templates/macros.rs +++ b/contrib/src/templates/macros.rs @@ -2,19 +2,21 @@ /// engines from the set of template engined passed in. macro_rules! engine_set { ($($feature:expr => $engine:ident),+,) => ({ - use std::collections::HashSet; - let mut set = HashSet::new(); + type RegisterFn = for<'a, 'b> unsafe fn(&'a [(&'b str, &TemplateInfo)]) -> bool; + + let mut set = Vec::new(); $( #[cfg(feature = $feature)] - fn $engine(set: &mut HashSet) { - set.insert($engine::EXT.to_string()); + fn $engine(set: &mut Vec<(&'static str, RegisterFn)>) { + set.push(($engine::EXT, $engine::register)); } #[cfg(not(feature = $feature))] - fn $engine(_: &mut HashSet) { } + fn $engine(_: &mut Vec<(&'static str, RegisterFn)>) { } $engine(&mut set); )+ + set }); } @@ -25,25 +27,27 @@ macro_rules! engine_set { /// 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(name: &str, info: &TemplateInfo, c: &T) - -> Option