From a361bbd89938d6e25ba57fb6f3b9bb0ad68eb75b Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 19 Aug 2021 19:43:05 -0700 Subject: [PATCH] Add 0.5 migration guide, FAQ to site docs. --- site/guide/01-upgrading-from-0.4.md | 807 ++++++++++++++++++++++++++++ site/guide/12-faq.md | 646 ++++++++++++++++++++++ site/guide/4-requests.md | 6 +- site/guide/5-responses.md | 4 +- site/guide/8-testing.md | 13 +- site/guide/9-configuration.md | 34 +- site/guide/index.md | 2 + site/tests/Cargo.toml | 1 - 8 files changed, 1486 insertions(+), 27 deletions(-) create mode 100644 site/guide/01-upgrading-from-0.4.md create mode 100644 site/guide/12-faq.md diff --git a/site/guide/01-upgrading-from-0.4.md b/site/guide/01-upgrading-from-0.4.md new file mode 100644 index 0000000000..1b362c6350 --- /dev/null +++ b/site/guide/01-upgrading-from-0.4.md @@ -0,0 +1,807 @@ +# Upgrading + +Rocket v0.5 bring many new features and improvements over Rocket v0.4. Along +with new features, Rocket v0.5 also includes many changes that improve the +overall usability, stability, and security of the framework and applications +written in it. While the Rust compiler can guide you through many of these +changes, others require special attention. The intent of this guide is to guide +you through these changes and more, migrating your Rocket application to 0.5 and +reaping the benefits of new features and improvements. + +This guide is _not_ intended to replace, but instead complement, a reading of +the [CHANGELOG]. The [CHANGELOG] should be considered required reading for all +developers wishing to migrate their applications to Rocket v0.5. + +[CHANGELOG]: @github/CHANGELOG.md + +## Crate Organization + +Rocket v0.5 incorporates an improved module structure and crate ecosystem. +Modules and items that have been moved or removed will trigger a compiler error. +We encourage users to search through the [CHANGELOG] or [API docs](@api/rocket) +for the v0.5 analog. All previously existing functionality, except for that +incompatible with async I/O, is available in v0.5. + +### Off-by-Default Secrets + +The `private-cookies` crate feature, which was previously enabled by default, +has been renamed to `secrets` and is disabled by default. If you are using +private cookies, you _must_ enable the `secrets` feature in `Cargo.toml`: + +```toml +[dependencies] +rocket = { version = "0.5.0-rc.1", features = ["secrets"] } +``` + +### Contrib Deprecation + +The `rocket_contrib` crate is deprecated and is wholly incompatible with Rocket +0.5. _All_ users of `rocket_contrib` _must_: + + * Remove all dependencies and references to `rocket_contrib`. + * For templating support, depend on the new [`rocket_dyn_templates`] crate. + * For database pools, depend on the new [`rocket_sync_db_pools`] and/or + [`rocket_db_pools`] crates. + * Enable [features in `rocket`] as necessary. + +For example, to make use of JSON and Tera templates, make the following changes +to `Cargo.toml`: + +```diff +[dependencies] +- rocket = "0.4" +- rocket_contrib = { version = "0.4", features = ["json"], default-features = false } ++ rocket = { version = "0.5.0-rc.1", features = ["json"] } ++ rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } +``` + +! note: `rocket_dyn_templates` _does not_ follow in version lock-step with + the `rocket` crate. + + This is intentional. The crate depends on many external dependencies which may + evolve at a different pace than Rocket itself. Allowing their versions to + diverge enables keeping dependencies up-to-date without breaking `rocket` + itself. + +All features previously present in `rocket_contrib` are available elsewhere. The +[contrib graduation] section of the CHANGELOG contains full details on the +changes. + + +[`rocket_dyn_templates`]: @api/rocket_dyn_templates +[`rocket_sync_db_pools`]: @api/rocket_sync_db_pools +[`rocket_db_pools`]: @api/rocket_db_pools +[features in `rocket`]: @api/rocket/#features +[contrib graduation]: @github/CHANGELOG.md#contrib-graduation + +## Stable and Async Support + +Rocket v0.5 compiles and builds on Rust stable with an entirely asynchronous +core. You are encouraged to: + + * Switch to the Rust stable release channel for production builds. + * Remove the previously required `#![feature(..)]` crate attribute. + +All application authors _must_: + + * Use `rocket::build()` instead of `rocket::ignite()`. + * Use either the `#[launch]` or `#[rocket::main]` async entry attribute. + * Use `async` versions of any blocking I/O or execute it in another thread. + +Application authors _may_: + + * Prefer to explicitly import macros via `use` instead of `#[macro_use]`. + +The rest of the section describes making these changes in detail. + +### Stable Release Channel + +If you prefer to use Rust's stable release channel, you can switch to it using +`rustup`: + +```sh +## switch globally +rustup default nightly + +## switch locally +rustup override set stable +``` + +Using the stable release channel ensures that _no_ breakages will occur when +upgrading your Rust compiler or Rocket. That being said, Rocket continues to +take advantage of features only present in the nightly channel. As a result, the +development experience will continue to be better on nightly for the forseeable +future. For example, compiler diagnostics on `nightly` are more detailed and +accurate: + +
+Example Diagnostic on Nightly + +```rust,ignore +error: invalid parameters for `has_two` route uri + --> $DIR/typed-uris-bad-params.rs:55:18 + | +55 | uri!(has_two(id = 100, cookies = "hi")); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: uri parameters are: id: i32, name: String + = help: missing parameter: `name` +help: unknown parameter: `cookies` + --> $DIR/typed-uris-bad-params.rs:55:28 + | +55 | uri!(has_two(id = 100, cookies = "hi")); + | ^^^^^^^ +``` + +
+ +
+Example Diagnostic on Stable + +```rust,ignore +error: invalid parameters for `has_two` route uri + --- note: uri parameters are: id: i32, name: String + --- help: missing parameter: `name` + --> $DIR/typed-uris-bad-params.rs:55:18 + | +55 | uri!(has_two(id = 100, cookies = "hi")); + | ^^ + +error: [help] unknown parameter: `cookies` + --> $DIR/typed-uris-bad-params.rs:55:28 + | +55 | uri!(has_two(id = 100, cookies = "hi")); + | ^^^^^^^ +``` + +
+ +Our **recommendation** is to develop locally on the nightly channel but build +and deploy for production on the stable channel. + +### Feature Attribute + +As a result support for the stable release channel, Rocket applications no +longer need to enable any features to be used. You should **remove any +`#[feature(..)]` crate attributes:** + +```diff +- #![feature(proc_macro_hygiene, decl_macro)] +- + #[macro_use] extern crate rocket; + + fn main() { .. } +``` + +### Updates to Launch + +The new asynchronous core requires an async runtime to run. The new +[`launch`] and [`main`] attributes simplify starting a runtime suitable for +running Rocket applications. You should use [`launch`] whenever possible. + +At the same time, the `rocket::ignite()` function has been renamed to +[`rocket::build()`]; calls to the function or method should be replaced +accordingly. Together, these two changes result in the following diff to what +was previously the `main` function: + +```diff +- fn main() { +- rocket::ignite().mount("/hello", routes![hello]).launch(); +- } ++ #[launch] ++ fn rocket() -> _ { ++ rocket::build().mount("/hello", routes![hello]) ++ } +``` + +[`launch`]: @api/rocket/attr.launch.html +[`main`]: @api/rocket/attr.main.html +[`rocket::build()`]: @api/rocket/struct.Rocket.html#method.build + +### Blocking I/O + +Rocket v0.5 takes advantage of the latest developments in async I/O in Rust by +migrating to a fully asynchronous core powered by [`tokio`]. Specifically, +_every_ request is handled by an asynchronous task which internally calls one or +more request handlers. Asynchronous tasks are multiplexed on a [configurable +number of worker threads]. Though there is no limit on the number of tasks that +can run concurrently, at most `worker` tasks can be run in parallel. + +The runtime can switch between tasks in a single worker thread _iff_ an `await` +point in reached. In other words, context switching is _cooperative_, _not_ +preemptive. This _iff_ is critical: if an `await` point is _not_ reached, no +task switching can occur. As such, it is important that `await` points occur +periodically in a task so that tasks waiting to be scheduled are not starved. + +In general, when working with `async` APIs, await points will occur naturally. +However, an application written for synchronous I/O, like all Rocket +applications prior to 0.4, must take great care to convert all synchronous, +blocking I/O, to `async` I/O. This is because, as the name implies, blocking I/O +blocks a thread from making progress until the I/O result is available, meaning +that no tasks can be scheduled on the waiting thread, wasting valuable resources +and significantly degrading performance. + +Common sources of blocking I/O and their `async` replacements include: + + * Anything in `std::fs`: replace with `rocket::tokio::fs`. + * Anything in `std::sync`: replace with `rocket::tokio::sync`. + * Anything in `std::net`: replace with `rocket::tokio::net`. + * Anything in `std::io`: replace with `rocket::tokio::io`. + * Sleep or timers: replace with `rocket::tokio::time`. + * Any networking: replace with `rocket::tokio::net`. + * Any file system access: replace with `rocket::tokio::fs`. + +Unfortunately, the Rust compiler provides no support for identifying blocking +I/O via lints or compile-time checks: it is up to you to scan your application +for sources of blocking I/O and replace them with their `async` counterpart. If +no such counterpart exists, you must execute the I/O in its own thread by using +[`rocket::tokio::task::spawn_blocking`]. + +All of Rocket's I/O APIs have been updated to be `async`-safe. +This results in requiring `.await` calls for common APIs like [`NamedFile`]. To +use `.await` in a route, the handler must be marked with `async`: + +```rust +# use rocket::get; +use rocket::fs::NamedFile; + +#[get("/")] +async fn index() -> Option { + NamedFile::open("index.html").await.ok() +} +``` + +
+See a diff of the changes from v0.4. + +```diff +- use rocket::response::NamedFile; ++ use rocket::fs::NamedFile; + +#[get("/")] +- fn index() -> Option { +- NamedFile::open("index.html").ok() ++ async fn index() -> Option { ++ NamedFile::open("index.html").await.ok() +} +``` + +
+ +[`tokio`]: https://tokio.rs +[configurable number of worker threads]: ../configuration/#workers +[`NamedFile`]: @api/rocket/fs/struct.NamedFile.html +[`rocket::tokio::task::spawn_blocking`]: @tokio/task/fn.spawn_blocking.html + +### Blocking Compute + +By the same reasoning, performing large amounts of compute (really, just another +form of I/O) can prevent other tasks from executing in a timely manner. If you +are performing long computations in a handler, you should execute the +computation in its own thread, again using [`rocket::tokio::task::spawn_blocking`]: + +```rust +# use rocket::get; +use rocket::tokio::task; +use rocket::response::Debug; + +#[get("/")] +async fn exepensive() -> Result<(), Debug> { + let result = task::spawn_blocking(move || { + // perform the computation + }).await?; + + Ok(result) +} +``` + +### Async Traits + +To support `async` methods in traits, Rocket provides the [`async_trait`] +attribute. The attribute _must_ be applied to all implementations of _async +traits_ like [`FromRequest`] and [`Fairing`]: + +```diff +use rocket::request::{self, Request, FromRequest}; + ++ #[rocket::async_trait] +impl<'r> FromRequest<'r> for MyType { + type Error = MyError; + +- fn from_request(req: &'r Request<'_>) -> request::Outcome { ++ async fn from_request(req: &'r Request<'_>) -> request::Outcome { + /* .. */ + } +} +``` + +All trait documentation has been updated to call out such traits with an example +implementation that includes the invocation. The example implementation also +serves as better documentation for trait and trait method signatures than the +rustdocs. Because `async_trait` modifies these signatures, the rustdocs diverge +from what is written in source. For example, rustdoc renders: + +```rust,ignore +fn from_request<'life0, 'async_trait>( + request: &'r Request<'life0> +) -> Pin> + Send + 'async_trait>>; +``` + +...whereas the source looks like: + +```rust,ignore +async fn from_request(req: &'r Request<'_>) -> Outcome; +``` + +Unfortunately, rustdoc does not provide a mechanism to render the source as it +is written. As such, we encourage all authors to use the examples as the source +of truth for trait and method signatures. + +[`async_trait`]: @api/rocket/attr.async_trait.html +[`FromRequest`]: @api/rocket/request/trait.FromRequest.html +[`Fairing`]: @api/rocket/fairing/trait.Fairing.html + +## Configuration + +Rocket's configuration system has been entirely revamped for 0.5. The +[configuration](../configuration) section of the guide contains a full +walkthrough of the new system while the [general changes] section of the +CHANGELOG contains further details on configuration changes. We call out the +most important of these changes here. All users _must_: + + * Replace `ROCKET_ENV` environment variable use with `ROCKET_PROFILE`. + * Replace `ROCKET_LOG` environment variable with `ROCKET_LOG_LEVEL`. + * Use only IP addreses for the `address` configuration parameter. + * Replace the `dev` or `development` profile with `debug`. + * Note that the `stage`, `staging`, `prod`, and `production` profiles carry no + special meaning in v0.5. + * Use `0` to disable `keep_alive` instead of `false` or `off`. + * Replace uses of "extras" with [typed extraction]. + +Rocket will emit warnings at launch time if use of the previous functionality is +detected. + +### Profiles + +The new system deals with "profiles" where there were previously "environments". +As opposed to environments, profiles: + + * Can be arbitrarily named, and any number can exist. + * Match Rust profiles in naming: `debug` and `release` are the default + profiles for the respective Rust compilation profile. + * Are programmatically selectable and configurable. + * Have a `default` profile with fallback values for all profiles. + * Have a `global` profile with overrides for all profiles. + +Authors should read the new [configuration](../configuration) section of the +guide to determine the scope of changes required. This likely includes: + + * Defining most configuration in the `default` profile instead. + * Using the `debug` profile where `dev` or `development` was used. + * Using the `release` profile where `prod` or `production` was used. + +[general changes]: @github/CHANGELOG.md#general +[typed extraction]: ../configuration/#extracting-values + +### Typed Extraction + +The "extras" configuration in v0.4 is entirely replaced by [typed extraction], +which allows any `Deserialize` structure to be derived from configuration +sources. All users _should_ make use of typed extraction where "extras" were +being used previously. The diff below illustrates one such example: + +```diff +use rocket::fairing::AdHoc; + ++ #[derive(Deserialize)] +struct AppConfig { + id: Option, + port: u16, +} + +- fn main() { +- rocket::ignite() +- .attach(AdHoc::on_attach("Token Config", |rocket| { +- println!("Adding token managed state from config..."); +- let id = match rocket.config().get_int("id") { +- Ok(v) if v >= 0 => Some(v as usize), +- _ => None, +- }; +- +- let port = match rocket.config().get_int("port") { +- Ok(v) if v => 0 && v < 1 << 16 => v as u16, +- _ => return Err(rocket) +- }; +- +- Ok(rocket.manage(AppConfig { id, port })) +- })) +- } + ++ #[launch] ++ fn rocket() -> _ { ++ rocket::build().attach(AdHoc::config::()) ++ } +``` + +## Routing + +Rocket v0.5 brings several major changes that affect routing: + + 1. [Default ranking] is more precise, so fewer routes need manual ranking. + 2. Multi-segment route parameters (``) now match _zero_ or more + segments. + 3. Parameters are _always_ percent-decoded, so `&RawStr` no longer implements + `FromParam`. + 4. Query parameters parse with [`FromForm`] instead of `FromQuery` and support + arbitrarily collections, nesting, structures, etc. + 5. All UTF-8 characters are allowed in static path components: `#[get("/❤️")]`. + 6. The [`register()`] method require a path to [scope catchers] under. Using + `"/"` emulates the previous behavior. + +[Default ranking]: ../requests#default-ranking +[`FromForm`]: @api/rocket/form/trait.FromForm.html +[`FromParam`]: @api/rocket/request/trait.FromParam.html +[`register()`]: @api/rocket/struct.Rocket.html#method.register +[scope catchers]: ../requests/#scoping + +### Default Ranks + +Default route ranking now takes into account partially dynamic paths, increasing +the range of default ranks from `[-6, -1]` to `[-12, -1]`. The net effect is +that fewer routes collide by default, requiring less manual ranking. For +example, the following two routes collide in 0.4 but not in 0.5: + +```rust +# use rocket::get; + +#[get("/foo/<_>/bar")] +fn foo_bar() { } + +#[get("/<_..>")] +fn everything() { } +``` + +
+See a diff of the changes from v0.4. + +```diff +- #[get("/foo/<_>/bar", rank = 1)] ++ #[get("/foo/<_>/bar")] + fn foo_bar() { } + +- #[get("/<_..>", rank = 2)] ++ #[get("/<_..>")] + fn everything() { } +``` +
+ +**The recommendation** is to remove all unnecessary manual ranking parameters. +For smaller applications, you may find that _all_ manual ranks can be removed. +Larger applications may still require ranks to resolve ambiguities. + +### Kleen Multi-Segments + +The multi-segment route parameter `` now matches _zero or more_ segments, +a change from the previous _one_ or more segments. The implication is two-fold: + + 1. Where previously two routes were required to match a prefix and its + suffixes, now one suffices: + + ```diff + - #[get("/")] + - fn index(); + + - #[get("/")] + - fn rest(path: PathBuf); + + + #[get("/")] + + fn all(path: PathBuf); + ``` + + 2. A prefix collides with a route that matches all of its suffixes. For + example, `index` and `rest` above collide. + +Most applications will likely benefit from this change by allowing the extra +prefix-only route to be removed entirely. If the previous functionality of +requiring at least one segment is desired, a route that explicitly matches the +first segment can be used: + +```rust +# use std::path::PathBuf; +# use rocket::get; + +#[get("//")] +fn rest(first: PathBuf, rest: PathBuf) { /* .. */ } +``` + +### Fewer Raw Strings + +Rocket v0.5 makes a concerted effort to limit the exposure to strings from the +raw HTTP payload. In line with this philosophy, Rocket now percent-decodes all +incoming parameters automatically as opposed to doing so on-demand. The +corollary is three-fold: + + 1. The `&RawStr` type no longer implements [`FromParam`]. + 2. The `&str` type now implements [`FromParam`] and is fully decoded. + 3. The `String` parameter type is identical to the `&str` type and should be + avoided. + +Most applications can simply swap uses of `&RawStr` and `String` for `&str` in +routes, forms, and so on to benefit from the increase web-safety and +performance. For instance, the front-page example becomes: + +```diff + #[get("//")] +- fn hello(name: String, age: u8) -> String { ++ fn hello(name: &str, age: u8) -> String { + format!("Hello, {} year old named {}!", age, name) +} +``` + +A form that previously used `String` becomes: + +```diff +#[derive(FromForm)] +- struct MyForm { ++ struct MyForm<'r> { +- value: String, ++ value: &'r str, +} +``` + +### Queries as Forms + +Query strings in Rocket v0.5 are in parity with forms and support their [full +breadth](../requests#forms). Single segment query parameters (``) should +require little to no changes, except that they now support collections, +structures, and any other `FromForm` type. This implies that the majority, if +not _all_ custom `FromQuery` implementations, should be derivable via `FromForm` +or have a built-in equivalent like `Vec`: + +```rust +# use rocket::post; + +#[post("/?")] +fn form(numbers: Vec) { /* .. */ } +``` + +Multi-segment query parameters (``) no longer require the use of a +`Form` guard. Instead, `T` can be used directly: + +```diff +#[derive(FromForm)] +struct Person { /* .. */ } + +#[get("/hello?")] +- fn hello(person: Option>) ++ fn hello(person: Option) +``` + +## Forms + +Rocket v0.5 introduces entirely revamped [forms] with support for: + + * [Multipart uploads.](../requests#multipart) + * [Collections: maps, vectors, and more.](../requests#collections) + * [Nesting.](../requests#nesting) + * [Ad-Hoc validation.](../requests#ad-hoc-validation) + +Additionally, the [`FromForm` derive] has been substantially improved so that +nearly all custom implementations of `FromForm` or (the now defunct) +`FromFormValue` can be derived. Altogether, this means that any external crate +dependency for form handling and most custom `FromForm` or `FromFormValue` +implementations are unnecessary and should be removed. + +### Multipart + +If your application used an external crate to accept multipart form submissions, +the dependency should be removed: Rocket v0.5 natively handles multipart. A file +upload can be accepted via the [`TempFile`] form guard: + +```rust +# #[macro_use] extern crate rocket; + +use rocket::form::Form; +use rocket::fs::TempFile; + +#[derive(FromForm)] +struct Upload<'r> { + save: bool, + file: TempFile<'r>, +} + +#[post("/upload", data = "")] +fn upload(upload: Form>) { /* .. */ } +``` + +[`TempFile`]: @api/rocket/fs/enum.TempFile.html + +### Field Validation + +In Rocket v0.4, it was encouraged and often required to implement +`FromFormValue` to introduce typed field validation. In v0.5, this can be +accomplished by [deriving `FromForm`]: + +```diff +- use rocket::request::FromFormValue; +- use rocket::http::RawStr; +- +- struct AdultAge(usize); +- +- impl<'v> FromFormValue<'v> for AdultAge { +- type Error = &'v RawStr; +- +- fn from_form_value(form_value: &'v RawStr) -> Result { +- match form_value.parse::() { +- Ok(age) if age >= 21 => Ok(AdultAge(age)), +- _ => Err(form_value), +- } +- } +- } + ++ #[derive(FromForm)] ++ #[field(validate = range(21..))] ++ struct AdultAge(usize); +``` + +If a given validation is used once, a new type may offer no additional safety. +The validation can be performed directly on a field: + +```rust +use rocket::form::FromForm; + +#[derive(FromForm)] +struct MyForm { + #[field(validate = range(21..))] + age: usize, +} +``` + +[forms]: ../requests#forms +[`FromForm` derive]: @api/rocket/derive.FromForm.html +[deriving `FromForm`]: @api/rocket/derive.FromForm.html + +## Notable New Features + +Rocket v0.5 brings an abundance of new features that enable new functionality, +increase productivity, and make existing applications more robust. We encourage +all users to take advantage of these new features. + +### Sentinels + +Rocket v0.5 introduces [sentinels]. Entirely unique to Rocket, sentinels offer +an automatic last line of defense against runtime errors by enabling any type +that appears in a route to abort application launch if invalid conditions are +detected. For example, the [`&State`] guard in v0.5 is a [`Sentinel`] that +aborts launch if the type `T` is not in managed state, thus preventing +associated runtime errors. + +You should consider implementing `Sentinel` for your types if you have guards +(request, data, form, etc.) or responders that depend on `Rocket` state to +function properly. For example, consider a `MyResponder` that expects: + + * An error catcher to be registered for the `400` status code. + * A specific type `T` to be in managed state. + +Making `MyResponder` a sentinel that guards against these conditions is as +simple as: + +```rust +use rocket::{Rocket, Ignite, Sentinel}; +# struct MyResponder; +# struct T; + +impl Sentinel for MyResponder { + fn abort(r: &Rocket) -> bool { + !r.catchers().any(|c| c.code == Some(400)) || r.state::().is_none() + } +} +``` + +[sentinels]: @api/rocket/trait.Sentinel.html +[`Sentinel`]: @api/rocket/trait.Sentinel.html +[`&State`]: @api/rocket/struct.State.html + +### More Typed URIs + +Rocket v0.5 brings a completely overhauled [`uri!()`] macro and support for +typed URIs in more APIs. Notably, the `uri!()` macro now: + + * Allows URIs to be constructed from static values: + + ```rust + # use rocket::uri; + use rocket::http::uri::Absolute; + + const HOST: Absolute<'static> = uri!("http://localhost:8000"); + ``` + + * Allows static and dynamic [prefixes and suffixes] to route URIs to be + specified: + + ```rust + # use rocket::{uri, get}; + + #[get("/person/?")] + fn person(name: &str, age: Option) { } + + let uri = uri!("https://rocket.rs/", person("Bob", Some(28)), "#woo"); + assert_eq!(uri.to_string(), "https://rocket.rs/person/Bob?age=28#woo"); + + let host = uri!("http://bob.me"); + let uri = uri!(host, person("Bob", Some(28))); + assert_eq!(uri.to_string(), "http://bob.me/person/Bob?age=28"); + ``` + +APIs like [`Redirect`] and [`Client`] now accept typed URIs: + +```rust +# #[macro_use] extern crate rocket; + +use rocket::response::Redirect; + +#[get("/bye//")] +fn bye(name: &str, age: u8) -> Redirect { + Redirect::to(uri!("https://rocket.rs", bye(name, age), "?bye#now")) +} + +#[test] +fn test() { + use rocket::local::blocking::Client; + + let client = Client::new(rocket::build()); + let r = client.get(uri!(super::bye("Bob", 30))).dispatch(); +} +``` + +[URI types] have been overhauled accordingly. A new [`Reference`] type encodes +URI-references. Additionally, all URI types are now `Serialize` and +`Deserialize`, allowing URIs to be used in configuration and passed over the +wire. + +[`Redirect`]: @api/rocket/response/struct.Redirect.html +[`Client`]: @api/rocket/local/index.html +[prefixes and suffixes]: @api/rocket/macro.uri.html#prefixes-and-suffixes +[`uri!()`]: @api/rocket/macro.uri.html +[URI types]: @api/rocket/http/uri/index.html +[`Reference`]: @api/rocket/http/uri/struct.Reference.html + +### Real-Time Streams + +Rocket v0.5 introduces real-time, typed, `async` [streams]. The new [async +streams] section of the guide contains further details, and we encourage all +interested parties to see the new real-time, multi-room [chat example]. + +As a taste of what's possible, the following `stream` route emits a `"ping"` +Server-Sent Event every `n` seconds, defaulting to `1`: + +```rust +# use rocket::*; +use rocket::response::stream::{Event, EventStream};; +use rocket::tokio::time::{interval, Duration}; + +#[get("/ping?")] +fn stream(n: Option) -> EventStream![] { + EventStream! { + let mut timer = interval(Duration::from_secs(n.unwrap_or(1))); + loop { + yield Event::data("ping"); + timer.tick().await; + } + } +} +``` + +[streams]: @api/rocket/response/stream/index.html +[async streams]: ../responses/#async-streams +[chat example]: @example/chat + +## Getting Help + +If you run into any issues upgrading, we encourage you to ask questions via +[GitHub discussions] or via chat at [`#rocket:mozilla.org`] on Matrix or the +bridged [`#rocket`] IRC channel at `irc.libera.chat`. + +[GitHub discussions]: https://github.com/SergioBenitez/Rocket/discussions +[`#rocket:mozilla.org`]: https://chat.mozilla.org/#/room/#rocket:mozilla.org +[`#rocket`]: https://kiwiirc.com/client/irc.libera.chat/#rocket diff --git a/site/guide/12-faq.md b/site/guide/12-faq.md new file mode 100644 index 0000000000..989c38b4f3 --- /dev/null +++ b/site/guide/12-faq.md @@ -0,0 +1,646 @@ +# FAQ + +Below you'll find a collection of commonly asked questions and answers. If you +have suggestions for questions you'd like to see answered here, [comment on the +discussion thread]. + +[comment on the discussion thread]: https://github.com/SergioBenitez/Rocket/discussions/1836 + +## About Rocket + +
+ +Is Rocket a monolithic framework like Rails? Or is it more like Flask? +# + +
+ +Neither! + +Rocket's core is small yet complete when it comes to security and correctness. +It mainly consists of: + + * Guard traits like [`FromRequest`] and [`FromData`]. + * Derive macros for all commonly used traits. + * Attribute macros for routing. + * Thorough compile and launch-time checking. + * Optional features to enable functionality like TLS, secrets, and so on. + * Zero-copy parsers and validators for common formats like multipart and SSE. + * Syntax sugar extensions for features like async streams and traits. + +The goal is for functionality like templating, sessions, ORMs, and so on to be +implemented entirely outside of Rocket and yet feel first-class. Indeed, crates +like [`rocket_dyn_templates`] and [`rocket_db_pools`] do just this. + +As a result, Rocket is neither "bare-bones" nor is it a kitchen sink for all +possible features. Unlike other frameworks in the Rust ecosystem, Rocket makes +it its mission to help you avoid security and correctness blunders +out-of-the-box. It does this by including, out-of-the-box: + + * A flexible, type-based configuration system. + * Security and privacy headers by default. + * Zero-Copy RFC compliant URI parsers. + * Safe, typed URIs with compile-time checking. + * Thorough compile-time and launch-time checking of routes. + * A complete testing framework with sync and `async` variants. + * Safe, exclusive access to fully decoded HTTP values. + * Mandatory data limits to prevent trivial DoS attacks. + +Of course, this functionality comes at a compile-time cost (but notably, _not_ +at a runtime cost), impacting Rocket's clean build-time. For comparison, here's +what building "Hello, world!" for the first time in popular Rust web frameworks +looks like: + +| Framework | Dependencies | Build Time | +|----------------------|--------------|------------| +| Rocket 0.5-rc.2 | 148 | 44s | +| Actix-Web 4.0-beta.8 | 175 | 47s | +| Tide 0.16 | 209 | 34s | +| Warp 0.3 | 148 | 37s | + +· Measurements taken on a MacBookPro15,1 Intel Core i9 @ 2.9GHZ, macOS +11.2.1, Rust 1.53 stable. Best of 3.
+· Rocket includes features like multipart parsing and static file +serving that would require additional deps in other frameworks. + +Of course, iterative build-time is nearly identical for all frameworks, and the +time can be further reduced by using faster linkers like `lld`. We think the +trade-off is worth it. Rocket will never compromise security, correctness, or +usability to "win" at benchmarks of any sort. + +
+
+ +[`rocket_dyn_templates`]: @api/rocket_dyn_templates +[`rocket_db_pools`]: @api/rocket_db_pools + +
+ +I want a small and compact web framework. Is Rocket it? +# + +
+ +We think so! See ["Is Rocket a monolithic framework like Rails?"](#monolithic) +
+
+ +
+ +I want a web framework with all the bells and whistles. Is Rocket it? +# + +
+ +We think so! See ["Is Rocket a monolithic framework like Rails?"](#monolithic) +
+
+ +
+ +Can I use Rocket in production? Should I? It's only v0.x! +# + +
+ +We **enthusiastically** recommend using Rocket in production, with the following +caveats: + + 1. Run Rocket behind a reverse proxy like HAProxy or in a production load + balancing environment. Rocket (Hyper) doesn't employ any defenses against + DDoS attacks or certain DoS attacks. + + 2. Use a TLS termination proxy for zero-downtown certificate rotation. + + 3. Properly configure your databases and database pools, especially with + respect to the pool size. + + 4. Ensure no blocking I/O happens outside of `spawn_blocking()` invocations. + +While Rocket _is_ still in the `0.x` phase, the version number is purely a +stylistic choice. In fact, we consider Rocket to be the most mature web +framework in the Rust ecosystem. To our knowledge, Rocket is the only Rust web +framework that correctly implements: + + * Server-Sent Events + * Graceful Shutdown + * Form Parsing with Arbitrarily Structure + * Zero-Copy, RFC Conforming URI Types + * Ambiguity-Free Routing + * Streamed Multipart Uploads + +If you're coming from a different ecosystem, you should feel comfortable +considering Rocket's `v0.x` as someone else's `vx.0`. Rust and Cargo's semver +policy, and Rocket's strict adherence to it, ensures that Rocket will _never_ +break your application when upgrading from `0.x.y` to `0.x.z`, where `z >= y`. +Furthermore, we backport _all_ security and correctness patches to the previous +major release (`0.{x-1}.y`), so your application remains secure if you need time +to upgrade. + +
+
+ +
+ +Is Rocket slow? Is Rocket fast? +# + +
+ +Rocket is pretty fast. + +A commonly repeated myth is that Rocket's great usability comes at the cost of +runtime performance. _**This is false.**_ Rocket's usability derives largely +from compile-time checks with _zero_ bearing on runtime performance. + +So what about benchmarks? Well, benchmarking is _hard_, and besides often being +conducted incorrectly, often appear to say more than they do. So, when you see a +benchmark for "Hello, world!", you should know that the benchmark's relevance +doesn't extend far beyond "Hello, world!" servers and the specific way the +measurement was taken. In other words, they provide only a baseline truth that +is hard to extrapolate to real-world use-cases, _your_ use-case. + +Nevertheless, here are some things you can consider as _generally_ true about +Rocket applications: + + * They'll perform much, _much_ better than those written in scripting + languages like Python or Ruby. + * They'll perform much better than those written in VM or JIT languages like + JavaScript or Java. + * They'll perform a bit better than those written in compiled but GC'd + languages like Go. + * They'll perform competitively with those written in compiled, non-GC'd + languages like Rust or C. + +Again, we emphasize _generally_ true. It is trivial to write a Rocket +application that is slower than a similar Python application. + +Besides a framework's _internal_ performance, you should also consider whether +it enables your _application itself_ to perform well. Rocket takes great care to +enable your application to perform as little work as possible through +unique-to-Rocket features like [managed state], [request-local state], and +zero-copy parsing and deserialization. + +
+
+ +[managed state]: ../state/#managed-state +[request-local state]: ../state/#request-local-state + +
+ +What are some examples of "big" apps written in Rocket? +# + +
+ +Here are some notable projects and websites in Rocket we're aware of: + + * [Vaultwarden] - A BitWarden Server + * [Conduit] - A Matrix Homeserver + * [Rust-Lang.org] - Rust Language Website + * [Plume] - Federated Blogging Engine + * [Hagrid] - OpenPGP KeyServer ([keys.openpgp.org](https://keys.openpgp.org/)) + * [SourceGraph Syntax Highlighter] - Syntax Highlighting API + +[Let us know] if you have a notable, public facing application written in Rocket +you'd like to see here! + +[Vaultwarden]: https://github.com/dani-garcia/vaultwarden +[Conduit]: https://conduit.rs/ +[Rust-Lang.org]: https://www.rust-lang.org/ +[Plume]: https://github.com/Plume-org/Plume +[Hagrid]: https://gitlab.com/hagrid-keyserver/hagrid/ +[SourceGraph Syntax Highlighter]: https://github.com/sourcegraph/syntect_server +[Let us know]: https://github.com/SergioBenitez/Rocket/discussions/categories/show-and-tell + +
+
+ +
+ +When will version `$y` be released? Why does it take so long? +# + +
+ +Rocket represents an ecosystem-wide effort to create a web framework that +enables writing web applications with unparalleled security, performance, and +usability. From design to implementation to documentation, Rocket is carefully +crafted to ensure the greatest productivity and reliability with the fewest +surprises. Our goal is to make Rocket the obvious choice across _all_ languages. + +Accomplishing this takes time, and our efforts extend to the entire ecosystem. +For example, work for Rocket v0.5 included: + + * [Fixing correctness issues in `x509-parser`.](https://github.com/rusticata/x509-parser/pull/90) + * [Reporting multiple](https://github.com/bikeshedder/deadpool/issues/114) + [correctness issues](https://github.com/bikeshedder/deadpool/issues/113) in `deadpool`. + * [Fixing a major usability issue in `async-stream`.](https://github.com/tokio-rs/async-stream/pull/57) + * [Creating a brand new configuration library.](https://github.com/SergioBenitez/Figment) + * [Updating](https://github.com/rousan/multer-rs/pull/21), + [fixing](https://github.com/rousan/multer-rs/pull/29), and + [maintaining](https://github.com/rousan/multer-rs/commit/2758e778e6aa2785b737c82fe45e58026bea2f01) `multer`. + * [Significantly improving `async_trait` correctness and usability.](https://github.com/dtolnay/async-trait/pull/143) + * [Porting `Pattern` APIs to stable.](https://github.com/SergioBenitez/stable-pattern) + * [Porting macro diagnostics to stable.](https://github.com/SergioBenitez/proc-macro2-diagnostics) + * [Creating a brand new byte unit library.](https://github.com/SergioBenitez/ubyte) + * [Fixing a bug in `rustc`'s `libtest`.](https://github.com/rust-lang/rust/pull/78227) + +A version of Rocket is released whenever it is feature-complete and exceeds +feature, security, and usability parity with the previous version. As a result, +specifying a release date is nearly impossible. We are _always_ willing to delay +a release if these properties are not readily evident. + +We know it can be frustrating, but we hope you'll agree that Rocket is worth the +wait. + +
+
+ +## How To + +
+ +Can I, and if so how, do I use WebSockets? +# + +
+ +Rocket doesn't support WebSockets quite yet. We're [working on it]. + +That being said, Rocket _does_ suport [Server-Sent Events], which allows for +real-time _unidirectional_ communication from the server to the client. This is +often sufficient for many of the applications that WebSockets are typically used +for. For instance, the [chat example] uses SSE to implement a real-time, +multiroom chat application. +
+
+ +[working on it]: https://github.com/SergioBenitez/Rocket/issues/90 +[Server-Sent Events]: @api/rocket/response/stream/struct.EventStream.html +[chat example]: @example/chat + +
+ +Should I use global state via something like `lazy_static!`? +# + +
+ +No. Rocket's [managed state] provides a better alternative. + +While it may be convenient or comfortable to use global state, the downsides are +numerous. They include: + + * The inability to test your application with different state. + * The inability to run your application on different threads with different + state. + * The inability to know the state a route accesses by looking at its + signature. +
+
+ +[managed state]: ../state/#managed-state + +
+ +How do I handle file uploads? What is this "multipart" in my stream? +# + +
+ +For a quick example on how to handle file uploads, see [multipart forms]. + +File uploads are transmitted by the browser as [multipart] form submissions, +which Rocket handles natively as a [`DataField`]. The [`TempFile`] form guard +can accept a `DataField` and stream the data to disk to then be persisted. +
+
+ +[multipart]: https://datatracker.ietf.org/doc/html/rfc7578 +[multipart forms]: ../requests/#multipart +[`DataField`]: @api/rocket/form/struct.DataField.html +[`TempFile`]: @api/rocket/fs/enum.TempFile.html + +
+ +How do I get an `&Request` in a handler? +# + +
+ +You don't! + +Rocket's [philosophy] is that as much of the request should be validated and +converted into useful typed values _before_ being processed. Allowing a +`Request` to be handled directly is incompatible with this idea. + +Instead, Rocket's handlers work through _guards_, reified as traits, which +validate and extract parts of a request as needed. Rocket automatically invokes +these guards for you, so custom guards are write-once-use-everywhere. Rocket +won't invoke handlers that depend on guards that fail. This way, handlers only +deal with fully validated, typed, secure values. + +Rocket provides all of the guard implementations you would expect +out-of-the-box, and you can implement your own, too. See the following: + + * Parameter Guards: [`FromParam`] + * Multi-Segment Guards: [`FromSegments`] + * Data Guards: [`FromData`] + * Form Guards: [`FromFrom`] + * Request Guards: [`FromRequest`] +
+
+ +[philosophy]: ../introduction/#foreword +[`FromParam`]: @api/rocket/request/trait.FromParam.html +[`FromSegments`]: @api/rocket/request/trait.FromSegments.html +[`FromData`]: @api/rocket/data/trait.FromData.html +[`FromFrom`]: @api/rocket/form/trait.FromForm.html +[`FromRequest`]: @api/rocket/request/trait.FromRequest.html + +
+ +How do I add a header to a response? +# + +
+ +That depends on the header! + +Any "transport" headers (`Content-Length`, `Transfer-Encoding`, etc.) are +automatically set by Rocket and cannot be directly overridden for correctness +reasons. The rest are set by a type's [`Responder`] implementation. + +**Status** + +Rocket automatically sets a Status header for all responses. If the `Responder` +doesn't explicitly set a status, it defaults to `200`. Responders like +`Option`, however, _do_ set the status. See the [`Responder`] docs for +details, and the [`status`] module for details on setting a custom Status or +overriding an existing one. + +**Content-Type** + +Rocket automatically sets a Content-Type header for most types it implements +`Responder` for, so in the common case, there's nothing to do. This includes +types like `&str`, `&[u8]`, `NamedFile`, and so on. The [`content`] module docs +have details on setting a custom Content-Type or overriding an existing one. + +**Everything Else** + +To add a custom header, you'll need a custom [`Responder`]. Not to worry! +[`Responder` can be derived](@api/rocket/derive.Responder.html) in almost all +cases. If a type for the header you want to add already exists, you can directly +derive `Responder` for a struct that contains the header value, which adds the +header to the response: + +```rust +# #[macro_use] extern crate rocket; +# use rocket::http::Header; + +# type HeaderType = Header<'static>; + +# impl From for MyResponder { +# fn from(inner: T) -> Self { +# MyResponder { inner, header: Header::new("X-My-Header", "some value") } +# } +# } + +#[derive(Responder)] +struct MyResponder { + inner: T, + header: HeaderType, +} + +#[get("/")] +fn with_header() -> MyResponder<&'static str> { + MyResponder::from("Hello, world!") +} +``` + +A `HeaderType` won't exist for custom headers, but you can define your own type. +As long as it implements `Into
` for Rocket's [`Header`], the type can be +used as a field in derived struct. + +You can always implement `Responder` directly. Make sure to leverage existing +responders in your implementation. For example, _don't_ serialize JSON manually. +Instead, use the existing [`Json`] responder, like in the example below: + +```rust +# #[derive(rocket::serde::Serialize)] +# #[serde(crate = "rocket::serde")] +# struct Person { name: String, age: usize }; + +use rocket::request::Request; +use rocket::response::{self, Response, Responder}; +use rocket::serde::json::Json; + +impl<'r> Responder<'r, 'static> for Person { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { + Response::build_from(Json(&self).respond_to(req)?) + .raw_header("X-Person-Name", self.name) + .raw_header("X-Person-Age", self.age.to_string()) + .ok() + } +} +``` + +
+
+ +[`Responder`]: @api/rocket/response/trait.Responder.html +[`content`]: @api/rocket/response/content/index.html +[`status`]: @api/rocket/response/status/index.html +[`Header`]: @api/rocket/http/struct.Header.html +[`Json`]: @api/rocket/serde/json/struct.Json.html + +
+ +How do I make one handler return different responses or status codes? +# + +
+ +If you're returning _two_ different responses, use a `Result` or an +[`Either`]. + +If you need to return _more_ than two kinds, [derive a custom `Responder`] `enum`: + +```rust +# use rocket::response::Responder; +use rocket::fs::NamedFile; +use rocket::http::ContentType; + +#[derive(Responder)] +enum Error<'r, T> { + #[response(status = 400)] + Unauthorized(T), + #[response(status = 404)] + NotFound(NamedFile), + #[response(status = 500)] + A(&'r str, ContentType), +} +``` + +
+
+ +[`Either`]: https://docs.rs/either/1/either/enum.Either.html +[derive a custom `Responder`]: @api/rocket/derive.Responder.html + +
+ +How do I make Rocket reload automatically when I change source code? +# + +
+ +In debug mode, Rocket automatically reloads templates for you. So if all you +need is live template reloading, Rocket's got you covered. + +For everything else, you'll need to use an external tool like [`cargo-watch`], +[`watchexec`] or [`entr`]. With `cargo-watch`, you can automatically rebuild and +run a Rocket application by executing: + +```sh +cargo watch -x run +``` + +To only restart on successful compilations, see [this note]. +
+
+ +[`cargo-watch`]: https://github.com/watchexec/cargo-watch +[`watchexec`]: https://github.com/watchexec/watchexec +[`entr`]: http://eradman.com/entrproject/ +[this note]: https://github.com/watchexec/cargo-watch/tree/b75ce2c260874dea480f4accfd46ab28709ec56a#restarting-an-application-only-if-the-buildcheck-succeeds + +
+ +How do I access managed state outside of a Rocket-related context? +# + +
+ +Use an `Arc`, like this: + +```rust +# use rocket::*; +use std::sync::Arc; + +#[launch] +fn rocket() -> _ { + # struct MyState; + let state = Arc::new(MyState); + + let external = state.clone(); + std::thread::spawn(move || { + let use_state = external; + }); + + rocket::build().manage(state) +} +``` + +
+
+ +
+ +How do I make Rocket a _part_ of my application as opposed to the whole thing? +# + +
+ +If you're developing an application where an HTTP server is a _part_ of the +application instead of being the entire thing, use the `#[main]` attribute and +manually call [`launch()`]: + + +```rust,no_run +#[rocket::main] +async fn main() { + # let should_start_server = false; + if should_start_server { + let result = rocket::build().launch().await; + } else { + // do something else + } +} +``` + +The cost to using the attribute is imperceptible and guarantees compatibility +with Rocket's async I/O. + +
+
+ +[`launch()`]: @api/rocket/struct.Rocket.html#method.launch + +## Debugging + +
+ +Is example `foo` broken? It doesn't work for me. +# + +
+ +Almost certainly not. + +Every example and code snippet you see in published documentation is tested by +the CI on every commit, and we only publish docs that pass the CI. Unless the CI +environment is broken, the examples _cannot_ be wrong. + +Common mistakes when running examples include: + + * Looking at an example for version `y` but depending on version `x`. Select + the proper git tag! + * Looking at outdated examples on StackOverflow or Google. Check the + date/version! + * Not configuring the correct dependencies. See the example's `Cargo.toml`! +
+
+ +
+ +The trait bound `rocket::Responder` (`FromRequest`, etc.) is not satisfied. +# + +
+ +If you're fairly certain a type implements a given Rocket trait but still get an +error like: + +```rust,ignore +error[E0277]: the trait bound `Foo: Responder<'_, '_>` is not satisfied + --> src\main.rs:4:20 + | +4 | fn foo() -> Foo + | ^^^ the trait `Responder<'_, '_>` is not implemented for `Foo` + | + = note: required by `respond_to` +``` + +...then you're almost certainly depending on libraries which depend on different +versions of `rocket`. A common mistake is to depend on a `contrib` library from +git while also depending on a `crates.io` version of Rocket or vice-versa: + +```toml +rocket = "0.5.0-rc.1" +rocket_db_pools = { git = "https://github.com/SergioBenitez/Rocket.git" } +``` + +This is _never_ correct. In Rust, types from two different versions of a library +or from different providers (like `git` vs. `crates.io`) are _always_ considered +distinct, even if they have the same name. Therefore, even if a type implements +a trait from one library, it _does not_ implement the trait from the other +library (since it is considered to be a _different_, _distinct_ library). In +other words, you can _never_ mix two different published versions of Rocket, a +published version and a `git` version, or two instances from different `git` +revisions. + +
+
diff --git a/site/guide/4-requests.md b/site/guide/4-requests.md index 384bed3719..9f2f4fc92b 100644 --- a/site/guide/4-requests.md +++ b/site/guide/4-requests.md @@ -1028,8 +1028,8 @@ you might write: ```rust # #[macro_use] extern crate rocket; -extern crate time; +use rocket::time::Date; use rocket::form::{self, Error}; #[derive(FromForm)] @@ -1038,10 +1038,10 @@ struct CreditCard { number: u64, #[field(validate = range(..9999))] cvv: u16, - expiration: time::Date, + expiration: Date, } -fn luhn<'v>(number: &u64, cvv: u16, exp: &time::Date) -> form::Result<'v, ()> { +fn luhn<'v>(number: &u64, cvv: u16, exp: &Date) -> form::Result<'v, ()> { # let valid = false; if !valid { Err(Error::validation("invalid credit card number"))?; diff --git a/site/guide/5-responses.md b/site/guide/5-responses.md index c1a744c4b6..bfa6b3326e 100644 --- a/site/guide/5-responses.md +++ b/site/guide/5-responses.md @@ -352,14 +352,14 @@ returns an infinite [`TextStream`] that produces one `"hello"` every second: ```rust # use rocket::get; -use rocket::tokio::time::{self, Duration}; +use rocket::tokio::time::{Duration, interval}; use rocket::response::stream::TextStream; /// Produce an infinite series of `"hello"`s, one per second. #[get("/infinite-hellos")] fn hello() -> TextStream![&'static str] { TextStream! { - let mut interval = time::interval(Duration::from_secs(1)); + let mut interval = interval(Duration::from_secs(1)); loop { yield "hello"; interval.tick().await; diff --git a/site/guide/8-testing.md b/site/guide/8-testing.md index ce18159944..2b62ea2d03 100644 --- a/site/guide/8-testing.md +++ b/site/guide/8-testing.md @@ -111,7 +111,7 @@ use rocket::http::{ContentType, Status}; # let rocket = rocket::build().mount("/", routes![hello]); # let client = Client::debug(rocket).expect("valid rocket instance"); -let mut response = client.get("/").dispatch(); +let mut response = client.get(uri!(hello)).dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::Plain)); @@ -187,13 +187,18 @@ Then, we create a new `GET /` request and dispatch it, getting back our application's response: ```rust +# use rocket::uri; # #[rocket::launch] # fn rocket() -> _ { # rocket::build().configure(rocket::Config::debug_default()) # } + +# #[rocket::get("/")] +# fn hello() -> &'static str { "Hello, world!" } + # use rocket::local::blocking::Client; # let client = Client::tracked(rocket()).expect("valid rocket instance"); -let mut response = client.get("/").dispatch(); +let mut response = client.get(uri!(hello)).dispatch(); ``` Finally, we ensure that the response contains the information we expect it to. @@ -215,7 +220,7 @@ use rocket::http::{ContentType, Status}; # # let rocket = rocket::build().mount("/", routes![hello]); # let client = Client::debug(rocket).expect("valid rocket instance"); -# let mut response = client.get("/").dispatch(); +# let mut response = client.get(uri!(hello)).dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.into_string(), Some("Hello, world!".into())); @@ -256,7 +261,7 @@ mod test { let client = Client::tracked(rocket()).expect("valid rocket instance"); # */ # let client = Client::debug(rocket()).expect("valid rocket instance"); - let mut response = client.get("/").dispatch(); + let mut response = client.get(uri!(super::hello)).dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.into_string().unwrap(), "Hello, world!"); } diff --git a/site/guide/9-configuration.md b/site/guide/9-configuration.md index 8998c2aef1..849d5ec01c 100644 --- a/site/guide/9-configuration.md +++ b/site/guide/9-configuration.md @@ -38,17 +38,16 @@ values: Configurations can be arbitrarily namespaced by [`Profile`]s. Rocket's [`Config`] and [`Config::figment()`] providers automatically set the configuration profile to "debug" when compiled in "debug" mode and "release" -when compiled in release mode. With the exception of `log_level`, which changes -from `normal` in debug to `critical` in release, all of the default -configuration values are the same in all profiles. What's more, all -configuration values _have_ defaults, so no configuration needs to be supplied -to get an application going. +when compiled in release mode, but you can arbitrarily name and set profiles to +your desire. For example, with the [default provider](#default-provider), you +can set the selected profile via `ROCKET_PROFILE`. This results in Rocket +preferring the values in the `ROCKET_PROFILE` profile. In addition to any profiles you declare, there are two meta-profiles, `default` and `global`, which can be used to provide values that apply to _all_ profiles. Values provided in a `default` profile are used as fall-back values when the -selected profile doesn't contain a requested values, while values in the -`global` profile supplant any values with the same name in any profile. +selected profile doesn't contain a requested value, while values in the `global` +profile supplant any values with the same name in any profile. [`Provider`]: @figment/trait.Provider.html [`Profile`]: @figment/struct.Profile.html @@ -71,22 +70,23 @@ selected profile doesn't contain a requested values, while values in the Rocket's default configuration provider is [`Config::figment()`]; this is the provider that's used when calling [`rocket::build()`]. -The default figment merges, at a per-key level, and reads from the following -sources, in ascending priority order: +The default figment reads from and merges, at a per-key level, the following +sources in ascending priority order: - 1. [`Config::default()`] - which provides default values for all parameters. + 1. [`Config::default()`], which provides default values for all parameters. 2. `Rocket.toml` _or_ TOML file path in `ROCKET_CONFIG` environment variable. 3. `ROCKET_` prefixed environment variables. The selected profile is the value of the `ROCKET_PROFILE` environment variable, or if it is not set, "debug" when compiled in debug mode and "release" when -compiled in release mode. +compiled in release mode. With the exception of `log_level`, which changes from +`normal` in debug to `critical` in release, all of the default configuration +values are the same in all profiles. What's more, all configuration values +_have_ defaults, so no configuration is needed to get started. -As a result, without any effort, Rocket's server can be configured via a -`Rocket.toml` file and/or via environment variables, the latter of which take -precedence over the former. Note that neither the file nor any environment -variables need to be present as [`Config::default()`] is a complete -configuration source. +As a result of `Config::figment()`, without any effort, Rocket can be configured +via a `Rocket.toml` file and/or via environment variables, the latter of which +take precedence over the former. [`Config::default()`]: @api/rocket/struct.Config.html#method.default @@ -421,7 +421,7 @@ more complex cases. crate. As such, you may need to import crates directly: ` - figment = { version = "0.9", features = ["env", "toml", "json"] } + figment = { version = "0.10", features = ["env", "toml", "json"] } ` As a first example, we override configuration values at runtime by merging diff --git a/site/guide/index.md b/site/guide/index.md index d9ce422b02..6f84aec4b0 100644 --- a/site/guide/index.md +++ b/site/guide/index.md @@ -30,6 +30,8 @@ aspect of Rocket. The sections are: Rocket. - **[Conclusion](conclusion/):** concludes the guide and discusses next steps for learning. + - **[FAQ](faq/):** answers to frequently asked questions about Rocket and + using it. ## Getting Help diff --git a/site/tests/Cargo.toml b/site/tests/Cargo.toml index 16f557d24f..e95491bfa9 100644 --- a/site/tests/Cargo.toml +++ b/site/tests/Cargo.toml @@ -13,7 +13,6 @@ rocket = { path = "../../core/lib", features = ["secrets", "json"] } serde = { version = "1.0", features = ["derive"] } rand = "0.8" figment = { version = "0.10", features = ["toml", "env"] } -time = "0.2" [dev-dependencies.rocket_dyn_templates] path = "../../contrib/dyn_templates"