So. Many. Changes. This is an insane commit: simultaneously one of the best (because of all the wonderful improvements!) and one of the worst (because it is just massive) in the project's history. Routing: * All UTF-8 characters are accepted everywhere in route paths. (#998) * `path` is now `uri` in `route` attribute: `#[route(GET, path = "..")]` becomes `#[route(GET, uri = "..")]`. Forms Revamp * All form related types now reside in a new `form` module. * Multipart forms are supported. (resolves #106) * Collections are supported in forms and queries. (resolves #205) * Nested structures in forms and queries are supported. (resolves #313) * Form fields can be ad-hoc validated with `#[field(validate = expr)]`. * `FromFormValue` is now `FromFormField`, blanket implements `FromForm`. * Form field values are always percent-decoded apriori. Temporary Files * A new `TempFile` data and form guard allows streaming data directly to a file which can then be persisted. * A new `temp_dir` config parameter specifies where to store `TempFile`. * The limits `file` and `file/$ext`, where `$ext` is the file extension, determines the data limit for a `TempFile`. Capped * A new `Capped` type is used to indicate when data has been truncated due to incoming data limits. It allows checking whether data is complete or truncated. * `DataStream` methods return `Capped` types. * `DataStream` API has been revamped to account for `Capped` types. * Several `Capped<T>` types implement `FromData`, `FromForm`. * HTTP 413 (Payload Too Large) errors are now returned when data limits are exceeded. (resolves #972) Hierarchical Limits * Data limits are now hierarchical, delimited with `/`. A limit of `a/b/c` falls back to `a/b` then `a`. Core * `&RawStr` no longer implements `FromParam`. * `&str` implements `FromParam`, `FromData`, `FromForm`. * `FromTransformedData` was removed. * `FromData` gained a lifetime for use with request-local data. * The default error HTML is more compact. * `&Config` is a request guard. * The `DataStream` interface was entirely revamped. * `State` is only exported via `rocket::State`. * A `request::local_cache!()` macro was added for storing values in request-local cache without consideration for type uniqueness by using a locally generated anonymous type. * `Request::get_param()` is now `Request::param()`. * `Request::get_segments()` is now `Request::segments()`, takes a range. * `Request::get_query_value()` is now `Request::query_value()`, can parse any `FromForm` including sequences. * `std::io::Error` implements `Responder` like `Debug<std::io::Error>`. * `(Status, R)` where `R: Responder` implements `Responder` by overriding the `Status` of `R`. * The name of a route is printed first during route matching. * `FlashMessage` now only has one lifetime generic. HTTP * `RawStr` implements `serde::{Serialize, Deserialize}`. * `RawStr` implements _many_ more methods, in particular, those related to the `Pattern` API. * `RawStr::from_str()` is now `RawStr::new()`. * `RawStr::url_decode()` and `RawStr::url_decode_lossy()` only allocate as necessary, return `Cow`. * `Status` implements `Default` with `Status::Ok`. * `Status` implements `PartialEq`, `Eq`, `Hash`, `PartialOrd`, `Ord`. * Authority and origin part of `Absolute` can be modified with new `Absolute::{with,set}_authority()`, `Absolute::{with,set}_origin()` methods. * `Origin::segments()` was removed in favor of methods split into query and path parts and into raw and decoded versions. * The `Segments` iterator is smarter, returns decoded `&str` items. * `Segments::into_path_buf()` is now `Segments::to_path_buf()`. * A new `QuerySegments` is the analogous query segment iterator. * Once set, `expires` on private cookies is not overwritten. (resolves #1506) * `Origin::path()` and `Origin::query()` return `&RawStr`, not `&str`. Codegen * Preserve more spans in `uri!` macro. * Preserve spans `FromForm` field types. * All dynamic parameters in a query string must typecheck as `FromForm`. * `FromFormValue` derive removed; `FromFormField` added. * The `form` `FromForm` and `FromFormField` field attribute is now named `field`. `#[form(field = ..)]` is now `#[field(name = ..)]`. Contrib * `Json` implements `FromForm`. * `MsgPack` implements `FromForm`. * The `json!` macro is exported as `rocket_contrib::json::json!`. * Added clarifying docs to `StaticFiles`. Examples * `form_validation` and `form_kitchen_sink` removed in favor of `forms`. * The `hello_world` example uses unicode in paths. * The `json` example only allocates as necessary. Internal * Codegen uses new `exports` module with the following conventions: - Locals starts with `__` and are lowercased. - Rocket modules start with `_` and are lowercased. - `std` types start with `_` and are titlecased. - Rocket types are titlecased. * A `header` module was added to `http`, contains header types. * `SAFETY` is used as doc-string keyword for `unsafe` related comments. * The `Uri` parser no longer recognizes Rocket route URIs.
14 KiB
Configuration
Rocket's configuration system is flexible. Based on Figment, it allows you to configure your application the way you want while also providing with a sensible set of defaults.
Overview
Rocket's configuration system is based on Figment's Provider
s, types which
provide configuration data. Rocket's Config
and Config::figment()
, as
well as Figment's Toml
and Json
, are some examples of providers.
Providers can be combined into a single Figment
provider from which any
configuration structure that implements Deserialize
can be extracted.
Rocket expects to be able to extract a Config
structure from the provider it
is configured with. This means that no matter which configuration provider
Rocket is asked to use, it must be able to read the following configuration
values:
key | kind | description | debug/release default |
---|---|---|---|
address |
IpAddr |
IP address to serve on | 127.0.0.1 |
port |
u16 |
Port to serve on. | 8000 |
workers |
usize |
Number of threads to use for executing futures. | cpu core count |
keep_alive |
u32 |
Keep-alive timeout seconds; disabled when 0 . |
5 |
log_level |
LogLevel |
Max level to log. (off/normal/debug/critical) | normal /critical |
cli_colors |
bool |
Whether to use colors and emoji when logging. | true |
secret_key |
SecretKey |
Secret key for signing and encrypting values. | None |
tls |
TlsConfig |
TLS configuration, if any. | None |
tls.key |
&[u8] /&Path |
Path/bytes to DER-encoded ASN.1 PKCS#1/#8 key. | |
tls.certs |
&[u8] /&Path |
Path/bytes to DER-encoded X.509 TLS cert chain. | |
limits |
Limits |
Streaming read size limits. | Limits::default() |
limits.$name |
&str /uint |
Read limit for $name . |
forms = "32KiB" |
ctrlc |
bool |
Whether ctrl-c initiates a server shutdown. |
true |
Profiles
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.
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.
Secret Key
The secret_key
parameter configures a cryptographic key to use when encrypting
application values. In particular, the key is used to encrypt private cookies,
which are available only when the secrets
crate feature is enabled.
When compiled in debug mode, a fresh key is generated automatically. In release
mode, Rocket requires you to set a secret key if the secrets
feature is
enabled. Failure to do so results in a hard error at launch time. The value of
the parameter may either be a 256-bit base64 or hex string or a slice of 32
bytes.
Limits
The limits
parameter configures the maximum amount of data Rocket will accept
for a given data type. The value is expected to be a dictionary table where each
key corresponds to a data type and each value corresponds to the maximum size in
bytes Rocket should accept for that type. Rocket can parse both integers
(32768
) or SI unit based strings ("32KiB"
) as limits.
By default, Rocket specifies a 32 KiB
limit for incoming forms. Since Rocket
requires specifying a read limit whenever data is read, external data guards may
also choose to have a configure limit via the limits
parameter. The
rocket_contrib::Json
type, for instance, uses the limits.json
parameter.
TLS
Rocket includes built-in, native support for TLS >= 1.2 (Transport Layer
Security). In order for TLS support to be enabled, Rocket must be compiled with
the "tls"
feature:
[dependencies]
rocket = { version = "0.5.0-dev", features = ["tls"] }
TLS is configured through the tls
configuration parameter. The value of tls
is a dictionary with two keys: certs
and key
, described in the table above.
Each key's value may be either a path to a file or raw bytes corresponding to
the expected value. When a path is configured in a file source, such as
Rocket.toml
, relative paths are interpreted as being relative to the source
file's directory.
! warning: Rocket's built-in TLS implements only TLS 1.2 and 1.3. As such, it may not be suitable for production use.
Workers
The workers
parameter sets the number of threads used for parallel task
execution; there is no limit to the number of concurrent tasks. Due to a
limitation in upstream async executers, unlike other values, the workers
configuration value cannot be reconfigured or be configured from sources other
than those provided by Config::figment()
, detailed below. In other words,
only the values set by the ROCKET_WORKERS
environment variable or in the
workers
property of Rocket.toml
will be considered - all other workers
values are ignored.
Default Provider
Rocket's default configuration provider is Config::figment()
; this is the
provider that's used when calling rocket::ignite()
.
The default figment merges, at a per-key level, and reads from the following sources, in ascending priority order:
Config::default()
- which provides default values for all parameters.Rocket.toml
or TOML file path inROCKET_CONFIG
environment variable.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.
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.
Rocket.toml
Rocket searches for Rocket.toml
or the filename in a ROCKET_CONFIG
environment variable starting at the current working directory. If it is not
found, the parent directory, its parent, and so on, are searched until the file
is found or the root is reached. If the path set in ROCKET_CONFIG
is absolute,
no such search occurs, and the set path is used directly.
The file is assumed to be nested, so each top-level key declares a profile and its values the value for the profile. The following is an example of what such a file might look like:
## defaults for _all_ profiles
[default]
address = "0.0.0.0"
limits = { forms = "64 kB", json = "1 MiB" }
## set only when compiled in debug mode, i.e, `cargo build`
[debug]
port = 8000
## only the `json` key from `default` will be overridden; `forms` will remain
limits = { json = "10MiB" }
## set only when the `nyc` profile is selected
[nyc]
port = 9001
## set only when compiled in release mode, i.e, `cargo build --release`
## don't use this secret_key! generate your own and keep it private!
[release]
port = 9999
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
Environment Variables
Rocket reads all environment variable names prefixed with ROCKET_
using the
string after the _
as the name of a configuration value as the value of the
parameter as the value itself. Environment variables take precedence over values
in Rocket.toml
. Values are parsed as loose form of TOML syntax. Consider the
following examples:
ROCKET_INTEGER=1
ROCKET_FLOAT=3.14
ROCKET_STRING=Hello
ROCKET_STRING="Hello"
ROCKET_BOOL=true
ROCKET_ARRAY=[1,"b",3.14]
ROCKET_DICT={key="abc",val=123}
Extracting Values
Your application can extract any configuration that implements Deserialize
from the configured provider, which is exposed via Rocket::figment()
:
# #[macro_use] extern crate rocket;
# extern crate serde;
use serde::Deserialize;
#[launch]
fn rocket() -> _ {
let rocket = rocket::ignite();
let figment = rocket.figment();
#[derive(Deserialize)]
struct Config {
port: u16,
custom: Vec<String>,
}
// extract the entire config any `Deserialize` value
let config: Config = figment.extract().expect("config");
// or a piece of it into any `Deserialize` value
let custom: Vec<String> = figment.extract_inner("custom").expect("custom");
rocket
}
Both values recognized by Rocket and values not recognized by Rocket can be
extracted. This means you can configure values recognized by your application in
Rocket's configuration sources directly. The next section describes how you can
customize configuration sources by supplying your own Provider
.
Because it is common to store configuration in managed state, Rocket provides an
AdHoc
fairing that 1) extracts a configuration from the configured provider,
2) pretty prints any errors, and 3) stores the value in managed state:
# #[macro_use] extern crate rocket;
# extern crate serde;
# use serde::Deserialize;
# #[derive(Deserialize)]
# struct Config {
# port: u16,
# custom: Vec<String>,
# }
use rocket::{State, fairing::AdHoc};
#[get("/custom")]
fn custom(config: State<'_, Config>) -> String {
config.custom.get(0).cloned().unwrap_or("default".into())
}
#[launch]
fn rocket() -> _ {
rocket::ignite()
.mount("/", routes![custom])
.attach(AdHoc::config::<Config>())
}
Custom Providers
A custom provider can be set via rocket::custom()
, which replaces calls to
rocket::ignite()
. The configured provider can be built on top of
Config::figment()
, Config::default()
, both, or neither. The
Figment documentation has full details on instantiating existing
providers like Toml
and Json
as well as creating custom providers for
more complex cases.
! note: You may need to depend on figment
and serde
directly.
Rocket reexports figment
from its crate root, so you can refer to figment
types via rocket::figment
. However, Rocket does not enable all features from
the figment crate. As such, you may need to import figment
directly:
figment = { version = "0.9", features = ["env", "toml", "json"] }
Furthermore, you should directly depend on serde
when using its derive
feature, which is also not enabled by Rocket:
serde = { version = "1", features = ["derive"] }
As a first example, we override configuration values at runtime by merging figment's tuple providers with Rocket's default provider:
# #[macro_use] extern crate rocket;
use rocket::data::{Limits, ToByteUnit};
#[launch]
fn rocket() -> _ {
let figment = rocket::Config::figment()
.merge(("port", 1111))
.merge(("limits", Limits::new().limit("json", 2.mebibytes())));
rocket::custom(figment).mount("/", routes![/* .. */])
}
More involved, consider an application that wants to use Rocket's defaults for
Config
, but not its configuration sources, while allowing the application to
be configured via an App.toml
file that uses top-level keys as profiles
(.nested()
) and APP_
environment variables as global overrides
(.global()
):
# #[macro_use] extern crate rocket;
use serde::{Serialize, Deserialize};
use figment::{Figment, providers::{Format, Toml, Serialized, Env}};
use rocket::fairing::AdHoc;
#[derive(Debug, Deserialize, Serialize)]
struct Config {
app_value: usize,
/* and so on.. */
}
impl Default for Config {
fn default() -> Config {
Config { app_value: 3, }
}
}
#[launch]
fn rocket() -> _ {
let figment = Figment::from(rocket::Config::default())
.merge(Serialized::defaults(Config::default()))
.merge(Toml::file("App.toml").nested())
.merge(Env::prefixed("APP_").global());
rocket::custom(figment)
.mount("/", routes![/* .. */])
.attach(AdHoc::config::<Config>())
}
Rocket will extract it's configuration from the configured provider. This means
that if values like port
and address
are configured in Config
, App.toml
or APP_
environment variables, Rocket will make use of them. The application
can also extract its configuration, done here via the Adhoc::config()
fairing.