mirror of https://github.com/rwf2/Rocket.git
Finalize 'tracing' migration.
This commit complete the migration to 'tracing' for all logging. Below is a summary of all relevant commits, including this one: Log improvements: - All log (trace) messages are structured which means they contain fields that can formatted by any subscriber. - Logging can be disabled entirely by disabling the default `trace` feature. - Routes and catchers now contain location (file/line) information. - Two log format kinds: pretty and compact via ROCKET_LOG_FORMAT - Coloring is not disabled globally. Thus applications can color even if Rocket is configured not to. - Rocket is more conservative about 'warn' and 'error' messages, reserving those log levels for messages useful in production. - Errors from guards logged by codegen now use the 'Display' implementation of those errors when one exists. - Secrets are never logged, even when directly asked for. New features: - Many Rocket types know how to trace themselves via a new `Trace` trait. - `Either` types can now be used in `uri!()` calls. - A `RequestIdLayer` tags all requests with a unique ID. Breaking changes to configuration: - `Config::log_level` is of type `Option<Level>`. `None` disables tracing. - `log_level` now uses the traditional log level names: "off", "error", "warn", "info", "debug", "trace", or 0-5. This replace the Rocket-specific "normal", "debug", "critical". - A new option, `log_format`, which is either `compact` or `pretty`, determines how Rocket's tracing subscriber log trace messages. Breaking changes: - Hidden `rocket::Either` is now publicly available at `rocket::either::Either`. - `rocket::Error` no longer panics when dropped. - `main` generated by `#[launch]` returns an `ExitCode`. - `FromParam` `Err` now always returns the actual error as opposed to the string that failed to parse. To recover the original string, use `Either<T, &str>`, where `T: FromParam`, as a parameter guard. - Many types that implemented `Display` now instead implement `Trace`. - `Error::pretty_print()` was removed. Use `Error::trace()` via `Trace` impl. Internal improvements: - Made more space in CI machines for tasks. - Cleaned up testbench code using `inventory`. Resolves #21.
This commit is contained in:
parent
45264de8c9
commit
926e06ef3c
|
@ -1,5 +1,3 @@
|
||||||
use std::collections::hash_set::HashSet;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, Criterion};
|
use criterion::{criterion_group, Criterion};
|
||||||
|
|
||||||
use rocket::{route, config, Request, Data, Route, Config};
|
use rocket::{route, config, Request, Data, Route, Config};
|
||||||
|
@ -80,12 +78,12 @@ fn generate_matching_requests<'c>(client: &'c Client, routes: &[Route]) -> Vec<L
|
||||||
fn client(routes: Vec<Route>) -> Client {
|
fn client(routes: Vec<Route>) -> Client {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
profile: Config::RELEASE_PROFILE,
|
profile: Config::RELEASE_PROFILE,
|
||||||
// log_level: rocket::config::LogLevel::Off,
|
log_level: None,
|
||||||
cli_colors: config::CliColors::Never,
|
cli_colors: config::CliColors::Never,
|
||||||
shutdown: config::ShutdownConfig {
|
shutdown: config::ShutdownConfig {
|
||||||
ctrlc: false,
|
ctrlc: false,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
signals: HashSet::new(),
|
signals: std::collections::hash_set::HashSet::new(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -21,9 +21,9 @@ workspace = true
|
||||||
deadpool_postgres = ["deadpool-postgres", "deadpool"]
|
deadpool_postgres = ["deadpool-postgres", "deadpool"]
|
||||||
deadpool_redis = ["deadpool-redis", "deadpool"]
|
deadpool_redis = ["deadpool-redis", "deadpool"]
|
||||||
# sqlx features
|
# sqlx features
|
||||||
sqlx_mysql = ["sqlx", "sqlx/mysql"]
|
sqlx_mysql = ["sqlx", "sqlx/mysql", "log"]
|
||||||
sqlx_postgres = ["sqlx", "sqlx/postgres"]
|
sqlx_postgres = ["sqlx", "sqlx/postgres", "log"]
|
||||||
sqlx_sqlite = ["sqlx", "sqlx/sqlite"]
|
sqlx_sqlite = ["sqlx", "sqlx/sqlite", "log"]
|
||||||
sqlx_macros = ["sqlx/macros"]
|
sqlx_macros = ["sqlx/macros"]
|
||||||
# diesel features
|
# diesel features
|
||||||
diesel_postgres = ["diesel-async/postgres", "diesel-async/deadpool", "diesel", "deadpool_09"]
|
diesel_postgres = ["diesel-async/postgres", "diesel-async/deadpool", "diesel", "deadpool_09"]
|
||||||
|
@ -86,6 +86,11 @@ default-features = false
|
||||||
features = ["runtime-tokio-rustls"]
|
features = ["runtime-tokio-rustls"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.log]
|
||||||
|
version = "0.4"
|
||||||
|
default-features = false
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dev-dependencies.rocket]
|
[dev-dependencies.rocket]
|
||||||
path = "../../../core/lib"
|
path = "../../../core/lib"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
|
@ -269,7 +269,7 @@ mod deadpool_old {
|
||||||
mod sqlx {
|
mod sqlx {
|
||||||
use sqlx::ConnectOptions;
|
use sqlx::ConnectOptions;
|
||||||
use super::{Duration, Error, Config, Figment};
|
use super::{Duration, Error, Config, Figment};
|
||||||
// use rocket::config::LogLevel;
|
use rocket::tracing::level_filters::LevelFilter;
|
||||||
|
|
||||||
type Options<D> = <<D as sqlx::Database>::Connection as sqlx::Connection>::Options;
|
type Options<D> = <<D as sqlx::Database>::Connection as sqlx::Connection>::Options;
|
||||||
|
|
||||||
|
@ -301,12 +301,21 @@ mod sqlx {
|
||||||
specialize(&mut opts, &config);
|
specialize(&mut opts, &config);
|
||||||
|
|
||||||
opts = opts.disable_statement_logging();
|
opts = opts.disable_statement_logging();
|
||||||
// if let Ok(level) = figment.extract_inner::<LogLevel>(rocket::Config::LOG_LEVEL) {
|
if let Ok(value) = figment.find_value(rocket::Config::LOG_LEVEL) {
|
||||||
// if !matches!(level, LogLevel::Normal | LogLevel::Off) {
|
if let Some(level) = value.as_str().and_then(|v| v.parse().ok()) {
|
||||||
// opts = opts.log_statements(level.into())
|
let log_level = match level {
|
||||||
// .log_slow_statements(level.into(), Duration::default());
|
LevelFilter::OFF => log::LevelFilter::Off,
|
||||||
// }
|
LevelFilter::ERROR => log::LevelFilter::Error,
|
||||||
// }
|
LevelFilter::WARN => log::LevelFilter::Warn,
|
||||||
|
LevelFilter::INFO => log::LevelFilter::Info,
|
||||||
|
LevelFilter::DEBUG => log::LevelFilter::Debug,
|
||||||
|
LevelFilter::TRACE => log::LevelFilter::Trace,
|
||||||
|
};
|
||||||
|
|
||||||
|
opts = opts.log_statements(log_level)
|
||||||
|
.log_slow_statements(log_level, Duration::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sqlx::pool::PoolOptions::new()
|
sqlx::pool::PoolOptions::new()
|
||||||
.max_connections(config.max_connections as u32)
|
.max_connections(config.max_connections as u32)
|
||||||
|
|
|
@ -42,20 +42,20 @@ impl Engine for Environment<'static> {
|
||||||
Some(env)
|
Some(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<C: Serialize>(&self, name: &str, context: C) -> Option<String> {
|
fn render<C: Serialize>(&self, template: &str, context: C) -> Option<String> {
|
||||||
let Ok(template) = self.get_template(name) else {
|
let Ok(templ) = self.get_template(template) else {
|
||||||
error!("Minijinja template '{name}' was not found.");
|
error!(template, "requested template does not exist");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
match template.render(context) {
|
match templ.render(context) {
|
||||||
Ok(result) => Some(result),
|
Ok(result) => Some(result),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error_span!("Error rendering Minijinja template '{name}': {e}" => {
|
span_error!("templating", template, "failed to render Minijinja template" => {
|
||||||
let mut error = &e as &dyn std::error::Error;
|
let mut error = Some(&e as &dyn std::error::Error);
|
||||||
while let Some(source) = error.source() {
|
while let Some(err) = error {
|
||||||
error_!("caused by: {source}");
|
error!("{err}");
|
||||||
error = source;
|
error = err.source();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl Engine for Tera {
|
||||||
|
|
||||||
// Finally try to tell Tera about all of the templates.
|
// Finally try to tell Tera about all of the templates.
|
||||||
if let Err(e) = tera.add_template_files(files) {
|
if let Err(e) = tera.add_template_files(files) {
|
||||||
error_span!("Tera templating initialization failed" => {
|
span_error!("templating", "Tera templating initialization failed" => {
|
||||||
let mut error = Some(&e as &dyn Error);
|
let mut error = Some(&e as &dyn Error);
|
||||||
while let Some(err) = error {
|
while let Some(err) = error {
|
||||||
error!("{err}");
|
error!("{err}");
|
||||||
|
@ -48,7 +48,7 @@ impl Engine for Tera {
|
||||||
match Tera::render(self, template, &tera_ctx) {
|
match Tera::render(self, template, &tera_ctx) {
|
||||||
Ok(string) => Some(string),
|
Ok(string) => Some(string),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error_span!("failed to render Tera template {name}" [template] => {
|
span_error!("templating", template, "failed to render Tera template" => {
|
||||||
let mut error = Some(&e as &dyn Error);
|
let mut error = Some(&e as &dyn Error);
|
||||||
while let Some(err) = error {
|
while let Some(err) = error {
|
||||||
error!("{err}");
|
error!("{err}");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use rocket::{Rocket, Build, Orbit};
|
use rocket::{Rocket, Build, Orbit};
|
||||||
use rocket::fairing::{self, Fairing, Info, Kind};
|
use rocket::fairing::{self, Fairing, Info, Kind};
|
||||||
use rocket::figment::{Source, value::magic::RelativePathBuf};
|
use rocket::figment::{Source, value::magic::RelativePathBuf};
|
||||||
|
use rocket::trace::Trace;
|
||||||
|
|
||||||
use crate::context::{Callback, Context, ContextManager};
|
use crate::context::{Callback, Context, ContextManager};
|
||||||
use crate::template::DEFAULT_TEMPLATE_DIR;
|
use crate::template::DEFAULT_TEMPLATE_DIR;
|
||||||
|
@ -40,7 +41,7 @@ impl Fairing for TemplateFairing {
|
||||||
Ok(dir) => dir,
|
Ok(dir) => dir,
|
||||||
Err(e) if e.missing() => DEFAULT_TEMPLATE_DIR.into(),
|
Err(e) if e.missing() => DEFAULT_TEMPLATE_DIR.into(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
rocket::config::pretty_print_error(e);
|
e.trace_error();
|
||||||
return Err(rocket);
|
return Err(rocket);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -57,7 +58,7 @@ impl Fairing for TemplateFairing {
|
||||||
let cm = rocket.state::<ContextManager>()
|
let cm = rocket.state::<ContextManager>()
|
||||||
.expect("Template ContextManager registered in on_ignite");
|
.expect("Template ContextManager registered in on_ignite");
|
||||||
|
|
||||||
info_span!("templating" => {
|
span_info!("templating" => {
|
||||||
info!(directory = %Source::from(&*cm.context().root));
|
info!(directory = %Source::from(&*cm.context().root));
|
||||||
info!(engines = ?Engines::ENABLED_EXTENSIONS);
|
info!(engines = ?Engines::ENABLED_EXTENSIONS);
|
||||||
});
|
});
|
||||||
|
|
|
@ -248,7 +248,7 @@ impl Template {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let value = self.value.map_err(|e| {
|
let value = self.value.map_err(|e| {
|
||||||
error_span!("template context failed to serialize" => e.trace_error());
|
span_error!("templating", "template context failed to serialize" => e.trace_error());
|
||||||
Status::InternalServerError
|
Status::InternalServerError
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
|
||||||
let config = match Config::from(database, &rocket) {
|
let config = match Config::from(database, &rocket) {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error_span!("database configuration error" [database] => e.trace_error());
|
span_error!("database configuration error", database => e.trace_error());
|
||||||
return Err(rocket);
|
return Err(rocket);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -82,7 +82,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
})),
|
})),
|
||||||
Err(Error::Config(e)) => {
|
Err(Error::Config(e)) => {
|
||||||
error_span!("database configuration error" [database] => e.trace_error());
|
span_error!("database configuration error", database => e.trace_error());
|
||||||
Err(rocket)
|
Err(rocket)
|
||||||
}
|
}
|
||||||
Err(Error::Pool(reason)) => {
|
Err(Error::Pool(reason)) => {
|
||||||
|
|
|
@ -80,9 +80,10 @@ pub fn _catch(
|
||||||
}
|
}
|
||||||
|
|
||||||
#_catcher::StaticInfo {
|
#_catcher::StaticInfo {
|
||||||
name: stringify!(#user_catcher_fn_name),
|
name: ::core::stringify!(#user_catcher_fn_name),
|
||||||
code: #status_code,
|
code: #status_code,
|
||||||
handler: monomorphized_function,
|
handler: monomorphized_function,
|
||||||
|
location: (::core::file!(), ::core::line!(), ::core::column!()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,7 @@ impl EntryAttr for Launch {
|
||||||
// Always infer the type as `Rocket<Build>`.
|
// Always infer the type as `Rocket<Build>`.
|
||||||
if let syn::ReturnType::Type(_, ref mut ty) = &mut f.sig.output {
|
if let syn::ReturnType::Type(_, ref mut ty) = &mut f.sig.output {
|
||||||
if let syn::Type::Infer(_) = &mut **ty {
|
if let syn::Type::Infer(_) = &mut **ty {
|
||||||
let new = quote_spanned!(ty.span() => ::rocket::Rocket<::rocket::Build>);
|
*ty = syn::parse_quote_spanned!(ty.span() => ::rocket::Rocket<::rocket::Build>);
|
||||||
*ty = syn::parse2(new).expect("path is type");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +104,9 @@ impl EntryAttr for Launch {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (vis, mut sig) = (&f.vis, f.sig.clone());
|
let (vis, mut sig) = (&f.vis, f.sig.clone());
|
||||||
sig.ident = syn::Ident::new("main", sig.ident.span());
|
sig.ident = syn::Ident::new("main", f.sig.ident.span());
|
||||||
sig.output = syn::parse_quote!(-> #_ExitCode);
|
let ret_ty = _ExitCode.respanned(ty.span());
|
||||||
|
sig.output = syn::parse_quote_spanned!(ty.span() => -> #ret_ty);
|
||||||
sig.asyncness = None;
|
sig.asyncness = None;
|
||||||
|
|
||||||
Ok(quote_spanned!(block.span() =>
|
Ok(quote_spanned!(block.span() =>
|
||||||
|
|
|
@ -105,9 +105,10 @@ fn query_decls(route: &Route) -> Option<TokenStream> {
|
||||||
)*
|
)*
|
||||||
|
|
||||||
if !__e.is_empty() {
|
if !__e.is_empty() {
|
||||||
::rocket::info_span!("query string failed to match route declaration" => {
|
::rocket::trace::span_info!("codegen",
|
||||||
for _err in __e { ::rocket::info!("{_err}"); }
|
"query string failed to match route declaration" =>
|
||||||
});
|
{ for _err in __e { ::rocket::trace::info!("{_err}"); } }
|
||||||
|
);
|
||||||
|
|
||||||
return #Outcome::Forward((#__data, #Status::UnprocessableEntity));
|
return #Outcome::Forward((#__data, #Status::UnprocessableEntity));
|
||||||
}
|
}
|
||||||
|
@ -120,18 +121,25 @@ fn query_decls(route: &Route) -> Option<TokenStream> {
|
||||||
fn request_guard_decl(guard: &Guard) -> TokenStream {
|
fn request_guard_decl(guard: &Guard) -> TokenStream {
|
||||||
let (ident, ty) = (guard.fn_ident.rocketized(), &guard.ty);
|
let (ident, ty) = (guard.fn_ident.rocketized(), &guard.ty);
|
||||||
define_spanned_export!(ty.span() =>
|
define_spanned_export!(ty.span() =>
|
||||||
__req, __data, _request, FromRequest, Outcome
|
__req, __data, _request, display_hack, FromRequest, Outcome
|
||||||
);
|
);
|
||||||
|
|
||||||
quote_spanned! { ty.span() =>
|
quote_spanned! { ty.span() =>
|
||||||
let #ident: #ty = match <#ty as #FromRequest>::from_request(#__req).await {
|
let #ident: #ty = match <#ty as #FromRequest>::from_request(#__req).await {
|
||||||
#Outcome::Success(__v) => __v,
|
#Outcome::Success(__v) => __v,
|
||||||
#Outcome::Forward(__e) => {
|
#Outcome::Forward(__e) => {
|
||||||
::rocket::info!(type_name = stringify!(#ty), "guard forwarding");
|
::rocket::trace::info!(name: "forward", parameter = stringify!(#ident),
|
||||||
|
type_name = stringify!(#ty), status = __e.code,
|
||||||
|
"request guard forwarding");
|
||||||
|
|
||||||
return #Outcome::Forward((#__data, __e));
|
return #Outcome::Forward((#__data, __e));
|
||||||
},
|
},
|
||||||
|
#[allow(unreachable_code)]
|
||||||
#Outcome::Error((__c, __e)) => {
|
#Outcome::Error((__c, __e)) => {
|
||||||
::rocket::info!(type_name = stringify!(#ty), "guard failed: {__e:?}");
|
::rocket::trace::info!(name: "failure", parameter = stringify!(#ident),
|
||||||
|
type_name = stringify!(#ty), reason = %#display_hack!(__e),
|
||||||
|
"request guard failed");
|
||||||
|
|
||||||
return #Outcome::Error(__c);
|
return #Outcome::Error(__c);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -142,14 +150,14 @@ fn param_guard_decl(guard: &Guard) -> TokenStream {
|
||||||
let (i, name, ty) = (guard.index, &guard.name, &guard.ty);
|
let (i, name, ty) = (guard.index, &guard.name, &guard.ty);
|
||||||
define_spanned_export!(ty.span() =>
|
define_spanned_export!(ty.span() =>
|
||||||
__req, __data, _None, _Some, _Ok, _Err,
|
__req, __data, _None, _Some, _Ok, _Err,
|
||||||
Outcome, FromSegments, FromParam, Status
|
Outcome, FromSegments, FromParam, Status, display_hack
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returned when a dynamic parameter fails to parse.
|
// Returned when a dynamic parameter fails to parse.
|
||||||
let parse_error = quote!({
|
let parse_error = quote!({
|
||||||
::rocket::info!(name: "forward",
|
::rocket::trace::info!(name: "forward", parameter = #name,
|
||||||
reason = %__error, parameter = #name, "type" = stringify!(#ty),
|
type_name = stringify!(#ty), reason = %#display_hack!(__error),
|
||||||
"parameter forwarding");
|
"path guard forwarding");
|
||||||
|
|
||||||
#Outcome::Forward((#__data, #Status::UnprocessableEntity))
|
#Outcome::Forward((#__data, #Status::UnprocessableEntity))
|
||||||
});
|
});
|
||||||
|
@ -161,10 +169,11 @@ fn param_guard_decl(guard: &Guard) -> TokenStream {
|
||||||
match #__req.routed_segment(#i) {
|
match #__req.routed_segment(#i) {
|
||||||
#_Some(__s) => match <#ty as #FromParam>::from_param(__s) {
|
#_Some(__s) => match <#ty as #FromParam>::from_param(__s) {
|
||||||
#_Ok(__v) => __v,
|
#_Ok(__v) => __v,
|
||||||
|
#[allow(unreachable_code)]
|
||||||
#_Err(__error) => return #parse_error,
|
#_Err(__error) => return #parse_error,
|
||||||
},
|
},
|
||||||
#_None => {
|
#_None => {
|
||||||
::rocket::error!(
|
::rocket::trace::error!(
|
||||||
"Internal invariant broken: dyn param {} not found.\n\
|
"Internal invariant broken: dyn param {} not found.\n\
|
||||||
Please report this to the Rocket issue tracker.\n\
|
Please report this to the Rocket issue tracker.\n\
|
||||||
https://github.com/rwf2/Rocket/issues", #i);
|
https://github.com/rwf2/Rocket/issues", #i);
|
||||||
|
@ -176,6 +185,7 @@ fn param_guard_decl(guard: &Guard) -> TokenStream {
|
||||||
true => quote_spanned! { ty.span() =>
|
true => quote_spanned! { ty.span() =>
|
||||||
match <#ty as #FromSegments>::from_segments(#__req.routed_segments(#i..)) {
|
match <#ty as #FromSegments>::from_segments(#__req.routed_segments(#i..)) {
|
||||||
#_Ok(__v) => __v,
|
#_Ok(__v) => __v,
|
||||||
|
#[allow(unreachable_code)]
|
||||||
#_Err(__error) => return #parse_error,
|
#_Err(__error) => return #parse_error,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -187,17 +197,24 @@ fn param_guard_decl(guard: &Guard) -> TokenStream {
|
||||||
|
|
||||||
fn data_guard_decl(guard: &Guard) -> TokenStream {
|
fn data_guard_decl(guard: &Guard) -> TokenStream {
|
||||||
let (ident, ty) = (guard.fn_ident.rocketized(), &guard.ty);
|
let (ident, ty) = (guard.fn_ident.rocketized(), &guard.ty);
|
||||||
define_spanned_export!(ty.span() => __req, __data, FromData, Outcome);
|
define_spanned_export!(ty.span() => __req, __data, display_hack, FromData, Outcome);
|
||||||
|
|
||||||
quote_spanned! { ty.span() =>
|
quote_spanned! { ty.span() =>
|
||||||
let #ident: #ty = match <#ty as #FromData>::from_data(#__req, #__data).await {
|
let #ident: #ty = match <#ty as #FromData>::from_data(#__req, #__data).await {
|
||||||
#Outcome::Success(__d) => __d,
|
#Outcome::Success(__d) => __d,
|
||||||
#Outcome::Forward((__d, __e)) => {
|
#Outcome::Forward((__d, __e)) => {
|
||||||
::rocket::info!(type_name = stringify!(#ty), "data guard forwarding");
|
::rocket::trace::info!(name: "forward", parameter = stringify!(#ident),
|
||||||
|
type_name = stringify!(#ty), status = __e.code,
|
||||||
|
"data guard forwarding");
|
||||||
|
|
||||||
return #Outcome::Forward((__d, __e));
|
return #Outcome::Forward((__d, __e));
|
||||||
}
|
}
|
||||||
|
#[allow(unreachable_code)]
|
||||||
#Outcome::Error((__c, __e)) => {
|
#Outcome::Error((__c, __e)) => {
|
||||||
::rocket::info!(type_name = stringify!(#ty), "data guard failed: {__e:?}");
|
::rocket::trace::info!(name: "failure", parameter = stringify!(#ident),
|
||||||
|
type_name = stringify!(#ty), reason = %#display_hack!(__e),
|
||||||
|
"data guard failed");
|
||||||
|
|
||||||
return #Outcome::Error(__c);
|
return #Outcome::Error(__c);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -232,7 +249,7 @@ fn internal_uri_macro_decl(route: &Route) -> TokenStream {
|
||||||
/// Rocket generated URI macro.
|
/// Rocket generated URI macro.
|
||||||
macro_rules! #inner_macro_name {
|
macro_rules! #inner_macro_name {
|
||||||
($($token:tt)*) => {{
|
($($token:tt)*) => {{
|
||||||
rocket::rocket_internal_uri!(#route_uri, (#(#uri_args),*), $($token)*)
|
::rocket::rocket_internal_uri!(#route_uri, (#(#uri_args),*), $($token)*)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,6 +402,7 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
|
||||||
format: #format,
|
format: #format,
|
||||||
rank: #rank,
|
rank: #rank,
|
||||||
sentinels: #sentinels,
|
sentinels: #sentinels,
|
||||||
|
location: (::core::file!(), ::core::line!(), ::core::column!()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ define_exported_paths! {
|
||||||
_Vec => ::std::vec::Vec,
|
_Vec => ::std::vec::Vec,
|
||||||
_Cow => ::std::borrow::Cow,
|
_Cow => ::std::borrow::Cow,
|
||||||
_ExitCode => ::std::process::ExitCode,
|
_ExitCode => ::std::process::ExitCode,
|
||||||
|
display_hack => ::rocket::error::display_hack,
|
||||||
BorrowMut => ::std::borrow::BorrowMut,
|
BorrowMut => ::std::borrow::BorrowMut,
|
||||||
Outcome => ::rocket::outcome::Outcome,
|
Outcome => ::rocket::outcome::Outcome,
|
||||||
FromForm => ::rocket::form::FromForm,
|
FromForm => ::rocket::form::FromForm,
|
||||||
|
|
|
@ -1328,7 +1328,7 @@ pub fn catchers(input: TokenStream) -> TokenStream {
|
||||||
/// assert_eq!(bob2.to_string(), "/person/Bob%20Smith");
|
/// assert_eq!(bob2.to_string(), "/person/Bob%20Smith");
|
||||||
///
|
///
|
||||||
/// #[get("/person/<age>")]
|
/// #[get("/person/<age>")]
|
||||||
/// fn ok(age: Result<u8, &str>) { }
|
/// fn ok(age: Result<u8, std::num::ParseIntError>) { }
|
||||||
///
|
///
|
||||||
/// let kid1 = uri!(ok(age = 10));
|
/// let kid1 = uri!(ok(age = 10));
|
||||||
/// let kid2 = uri!(ok(12));
|
/// let kid2 = uri!(ok(12));
|
||||||
|
|
|
@ -709,3 +709,19 @@ fn test_vec_in_query() {
|
||||||
uri!(h(v = &[1, 2, 3][..])) => "/?v=%01%02%03",
|
uri!(h(v = &[1, 2, 3][..])) => "/?v=%01%02%03",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_either() {
|
||||||
|
use rocket::either::{Either, Left, Right};
|
||||||
|
|
||||||
|
#[get("/<_foo>")]
|
||||||
|
fn f(_foo: Either<usize, &str>) { }
|
||||||
|
|
||||||
|
assert_uri_eq! {
|
||||||
|
uri!(f(Left::<usize, &str>(123))) => "/123",
|
||||||
|
uri!(f(_foo = Left::<usize, &str>(710))) => "/710",
|
||||||
|
|
||||||
|
uri!(f(Right::<usize, &str>("hello world"))) => "/hello%20world",
|
||||||
|
uri!(f(_foo = Right::<usize, &str>("bye?"))) => "/bye%3F",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -114,14 +114,6 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks
|
||||||
73 | let _ = rocket::build().launch().await;
|
73 | let _ = rocket::build().launch().await;
|
||||||
| ^^^^^ only allowed inside `async` functions and blocks
|
| ^^^^^ only allowed inside `async` functions and blocks
|
||||||
|
|
||||||
error[E0728]: `await` is only allowed inside `async` functions and blocks
|
|
||||||
--> tests/ui-fail-nightly/async-entry.rs:73:42
|
|
||||||
|
|
|
||||||
72 | fn rocket() -> _ {
|
|
||||||
| ----------- this is not `async`
|
|
||||||
73 | let _ = rocket::build().launch().await;
|
|
||||||
| ^^^^^ only allowed inside `async` functions and blocks
|
|
||||||
|
|
||||||
error[E0277]: `main` has invalid return type `Rocket<Build>`
|
error[E0277]: `main` has invalid return type `Rocket<Build>`
|
||||||
--> tests/ui-fail-nightly/async-entry.rs:94:20
|
--> tests/ui-fail-nightly/async-entry.rs:94:20
|
||||||
|
|
|
|
||||||
|
|
|
@ -5,14 +5,14 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
&'a str
|
<&'a str as FromParam<'a>>
|
||||||
IpAddr
|
<IpAddr as FromParam<'a>>
|
||||||
Ipv4Addr
|
<Ipv4Addr as FromParam<'a>>
|
||||||
Ipv6Addr
|
<Ipv6Addr as FromParam<'a>>
|
||||||
NonZero<i128>
|
<NonZero<i128> as FromParam<'a>>
|
||||||
NonZero<i16>
|
<NonZero<i16> as FromParam<'a>>
|
||||||
NonZero<i32>
|
<NonZero<i32> as FromParam<'a>>
|
||||||
NonZero<i64>
|
<NonZero<i64> as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Q: FromSegments<'_>` is not satisfied
|
error[E0277]: the trait bound `Q: FromSegments<'_>` is not satisfied
|
||||||
|
@ -104,14 +104,14 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
&'a str
|
<&'a str as FromParam<'a>>
|
||||||
IpAddr
|
<IpAddr as FromParam<'a>>
|
||||||
Ipv4Addr
|
<Ipv4Addr as FromParam<'a>>
|
||||||
Ipv6Addr
|
<Ipv6Addr as FromParam<'a>>
|
||||||
NonZero<i128>
|
<NonZero<i128> as FromParam<'a>>
|
||||||
NonZero<i16>
|
<NonZero<i16> as FromParam<'a>>
|
||||||
NonZero<i32>
|
<NonZero<i32> as FromParam<'a>>
|
||||||
NonZero<i64>
|
<NonZero<i64> as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied
|
error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied
|
||||||
|
@ -138,14 +138,14 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
&'a str
|
<&'a str as FromParam<'a>>
|
||||||
IpAddr
|
<IpAddr as FromParam<'a>>
|
||||||
Ipv4Addr
|
<Ipv4Addr as FromParam<'a>>
|
||||||
Ipv6Addr
|
<Ipv6Addr as FromParam<'a>>
|
||||||
NonZero<i128>
|
<NonZero<i128> as FromParam<'a>>
|
||||||
NonZero<i16>
|
<NonZero<i16> as FromParam<'a>>
|
||||||
NonZero<i32>
|
<NonZero<i32> as FromParam<'a>>
|
||||||
NonZero<i64>
|
<NonZero<i64> as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
|
@ -155,12 +155,12 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
&'a str
|
<&'a str as FromParam<'a>>
|
||||||
IpAddr
|
<IpAddr as FromParam<'a>>
|
||||||
Ipv4Addr
|
<Ipv4Addr as FromParam<'a>>
|
||||||
Ipv6Addr
|
<Ipv6Addr as FromParam<'a>>
|
||||||
NonZero<i128>
|
<NonZero<i128> as FromParam<'a>>
|
||||||
NonZero<i16>
|
<NonZero<i16> as FromParam<'a>>
|
||||||
NonZero<i32>
|
<NonZero<i32> as FromParam<'a>>
|
||||||
NonZero<i64>
|
<NonZero<i64> as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
|
@ -86,7 +86,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Path, _>`
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:24:18
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:24:18
|
||||||
|
@ -190,7 +190,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>` is not satisfied
|
error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:41:29
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:41:29
|
||||||
|
@ -229,7 +229,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `S: Ignorable<rocket::http::uri::fmt::Query>` is not satisfied
|
error[E0277]: the trait bound `S: Ignorable<rocket::http::uri::fmt::Query>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:68:25
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:68:25
|
||||||
|
@ -298,7 +298,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut &'a std::path::Path>>
|
||||||
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
<&'a std::path::Path as FromUriParam<rocket::http::uri::fmt::Path, &'x mut PathBuf>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:15:15
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:15:15
|
||||||
|
@ -450,7 +450,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:49:17
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:49:17
|
||||||
|
@ -462,7 +462,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, i64>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, i64>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:51:22
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:51:22
|
||||||
|
@ -474,7 +474,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:58:25
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:58:25
|
||||||
|
@ -487,7 +487,7 @@ error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, s
|
||||||
<i32 as FromUriParam<P, &'x mut i32>>
|
<i32 as FromUriParam<P, &'x mut i32>>
|
||||||
<i32 as FromUriParam<P, i32>>
|
<i32 as FromUriParam<P, i32>>
|
||||||
= note: required for `std::option::Option<i32>` to implement `FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>`
|
= note: required for `std::option::Option<i32>` to implement `FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>`
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:60:19
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:60:19
|
||||||
|
@ -499,7 +499,7 @@ error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query
|
||||||
<isize as FromUriParam<P, &'x isize>>
|
<isize as FromUriParam<P, &'x isize>>
|
||||||
<isize as FromUriParam<P, &'x mut isize>>
|
<isize as FromUriParam<P, &'x mut isize>>
|
||||||
<isize as FromUriParam<P, isize>>
|
<isize as FromUriParam<P, isize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:62:24
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:62:24
|
||||||
|
@ -511,7 +511,7 @@ error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query
|
||||||
<isize as FromUriParam<P, &'x isize>>
|
<isize as FromUriParam<P, &'x isize>>
|
||||||
<isize as FromUriParam<P, &'x mut isize>>
|
<isize as FromUriParam<P, &'x mut isize>>
|
||||||
<isize as FromUriParam<P, isize>>
|
<isize as FromUriParam<P, isize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:79:40
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:79:40
|
||||||
|
@ -523,7 +523,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:80:33
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:80:33
|
||||||
|
@ -535,7 +535,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:83:25
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:83:25
|
||||||
|
@ -547,7 +547,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:84:25
|
--> tests/ui-fail-nightly/typed-uri-bad-type.rs:84:25
|
||||||
|
@ -559,4 +559,4 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
|
@ -10,7 +10,7 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
|
||||||
<Box<str> as Responder<'r, 'static>>
|
<Box<str> as Responder<'r, 'static>>
|
||||||
<Box<[u8]> as Responder<'r, 'static>>
|
<Box<[u8]> as Responder<'r, 'static>>
|
||||||
<Box<T> as Responder<'r, 'o>>
|
<Box<T> as Responder<'r, 'o>>
|
||||||
<rocket::Either<T, E> as Responder<'r, 'o>>
|
<rocket::either::Either<T, E> as Responder<'r, 'o>>
|
||||||
<Cow<'o, R> as Responder<'r, 'o>>
|
<Cow<'o, R> as Responder<'r, 'o>>
|
||||||
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
||||||
<EventStream<S> as Responder<'r, 'r>>
|
<EventStream<S> as Responder<'r, 'r>>
|
||||||
|
@ -29,7 +29,7 @@ error[E0277]: the trait bound `bool: Responder<'_, '_>` is not satisfied
|
||||||
<Box<str> as Responder<'r, 'static>>
|
<Box<str> as Responder<'r, 'static>>
|
||||||
<Box<[u8]> as Responder<'r, 'static>>
|
<Box<[u8]> as Responder<'r, 'static>>
|
||||||
<Box<T> as Responder<'r, 'o>>
|
<Box<T> as Responder<'r, 'o>>
|
||||||
<rocket::Either<T, E> as Responder<'r, 'o>>
|
<rocket::either::Either<T, E> as Responder<'r, 'o>>
|
||||||
<Cow<'o, R> as Responder<'r, 'o>>
|
<Cow<'o, R> as Responder<'r, 'o>>
|
||||||
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
||||||
<EventStream<S> as Responder<'r, 'r>>
|
<EventStream<S> as Responder<'r, 'r>>
|
||||||
|
@ -62,7 +62,7 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
|
||||||
<Box<str> as Responder<'r, 'static>>
|
<Box<str> as Responder<'r, 'static>>
|
||||||
<Box<[u8]> as Responder<'r, 'static>>
|
<Box<[u8]> as Responder<'r, 'static>>
|
||||||
<Box<T> as Responder<'r, 'o>>
|
<Box<T> as Responder<'r, 'o>>
|
||||||
<rocket::Either<T, E> as Responder<'r, 'o>>
|
<rocket::either::Either<T, E> as Responder<'r, 'o>>
|
||||||
<Cow<'o, R> as Responder<'r, 'o>>
|
<Cow<'o, R> as Responder<'r, 'o>>
|
||||||
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
||||||
<EventStream<S> as Responder<'r, 'r>>
|
<EventStream<S> as Responder<'r, 'r>>
|
||||||
|
@ -81,7 +81,7 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
|
||||||
<Box<str> as Responder<'r, 'static>>
|
<Box<str> as Responder<'r, 'static>>
|
||||||
<Box<[u8]> as Responder<'r, 'static>>
|
<Box<[u8]> as Responder<'r, 'static>>
|
||||||
<Box<T> as Responder<'r, 'o>>
|
<Box<T> as Responder<'r, 'o>>
|
||||||
<rocket::Either<T, E> as Responder<'r, 'o>>
|
<rocket::either::Either<T, E> as Responder<'r, 'o>>
|
||||||
<Cow<'o, R> as Responder<'r, 'o>>
|
<Cow<'o, R> as Responder<'r, 'o>>
|
||||||
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
<rocket::tokio::fs::File as Responder<'r, 'static>>
|
||||||
<EventStream<S> as Responder<'r, 'r>>
|
<EventStream<S> as Responder<'r, 'r>>
|
||||||
|
|
|
@ -137,9 +137,9 @@ error[E0277]: the trait bound `Unknown: FromForm<'r>` is not satisfied
|
||||||
<HashMap<K, V> as FromForm<'v>>
|
<HashMap<K, V> as FromForm<'v>>
|
||||||
<BTreeMap<K, V> as FromForm<'v>>
|
<BTreeMap<K, V> as FromForm<'v>>
|
||||||
<Arc<T> as FromForm<'v>>
|
<Arc<T> as FromForm<'v>>
|
||||||
|
<Vec<T> as FromForm<'v>>
|
||||||
<form::from_form::_::proxy::Range<T> as FromForm<'r>>
|
<form::from_form::_::proxy::Range<T> as FromForm<'r>>
|
||||||
<form::from_form::_::proxy::RangeFrom<T> as FromForm<'r>>
|
<form::from_form::_::proxy::RangeFrom<T> as FromForm<'r>>
|
||||||
<form::from_form::_::proxy::RangeTo<T> as FromForm<'r>>
|
|
||||||
and $N others
|
and $N others
|
||||||
= note: required for `Unknown` to implement `FromForm<'r>`
|
= note: required for `Unknown` to implement `FromForm<'r>`
|
||||||
|
|
||||||
|
@ -196,9 +196,9 @@ error[E0277]: the trait bound `Foo<usize>: FromForm<'r>` is not satisfied
|
||||||
<HashMap<K, V> as FromForm<'v>>
|
<HashMap<K, V> as FromForm<'v>>
|
||||||
<BTreeMap<K, V> as FromForm<'v>>
|
<BTreeMap<K, V> as FromForm<'v>>
|
||||||
<Arc<T> as FromForm<'v>>
|
<Arc<T> as FromForm<'v>>
|
||||||
|
<Vec<T> as FromForm<'v>>
|
||||||
<form::from_form::_::proxy::Range<T> as FromForm<'r>>
|
<form::from_form::_::proxy::Range<T> as FromForm<'r>>
|
||||||
<form::from_form::_::proxy::RangeFrom<T> as FromForm<'r>>
|
<form::from_form::_::proxy::RangeFrom<T> as FromForm<'r>>
|
||||||
<form::from_form::_::proxy::RangeTo<T> as FromForm<'r>>
|
|
||||||
and $N others
|
and $N others
|
||||||
= note: required for `Foo<usize>` to implement `FromForm<'r>`
|
= note: required for `Foo<usize>` to implement `FromForm<'r>`
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied
|
||||||
<Box<str> as Responder<'r, 'static>>
|
<Box<str> as Responder<'r, 'static>>
|
||||||
<Box<[u8]> as Responder<'r, 'static>>
|
<Box<[u8]> as Responder<'r, 'static>>
|
||||||
<Box<T> as Responder<'r, 'o>>
|
<Box<T> as Responder<'r, 'o>>
|
||||||
<rocket::Either<T, E> as Responder<'r, 'o>>
|
<rocket::either::Either<T, E> as Responder<'r, 'o>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Header<'_>: From<u8>` is not satisfied
|
error[E0277]: the trait bound `Header<'_>: From<u8>` is not satisfied
|
||||||
|
@ -52,7 +52,7 @@ error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied
|
||||||
<Box<str> as Responder<'r, 'static>>
|
<Box<str> as Responder<'r, 'static>>
|
||||||
<Box<[u8]> as Responder<'r, 'static>>
|
<Box<[u8]> as Responder<'r, 'static>>
|
||||||
<Box<T> as Responder<'r, 'o>>
|
<Box<T> as Responder<'r, 'o>>
|
||||||
<rocket::Either<T, E> as Responder<'r, 'o>>
|
<rocket::either::Either<T, E> as Responder<'r, 'o>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Header<'_>: From<u8>` is not satisfied
|
error[E0277]: the trait bound `Header<'_>: From<u8>` is not satisfied
|
||||||
|
@ -117,7 +117,7 @@ error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied
|
||||||
<Box<str> as Responder<'r, 'static>>
|
<Box<str> as Responder<'r, 'static>>
|
||||||
<Box<[u8]> as Responder<'r, 'static>>
|
<Box<[u8]> as Responder<'r, 'static>>
|
||||||
<Box<T> as Responder<'r, 'o>>
|
<Box<T> as Responder<'r, 'o>>
|
||||||
<rocket::Either<T, E> as Responder<'r, 'o>>
|
<rocket::either::Either<T, E> as Responder<'r, 'o>>
|
||||||
and $N others
|
and $N others
|
||||||
note: required by a bound in `route::handler::<impl Outcome<Response<'o>, Status, (rocket::Data<'o>, Status)>>::from`
|
note: required by a bound in `route::handler::<impl Outcome<Response<'o>, Status, (rocket::Data<'o>, Status)>>::from`
|
||||||
--> $WORKSPACE/core/lib/src/route/handler.rs
|
--> $WORKSPACE/core/lib/src/route/handler.rs
|
||||||
|
|
|
@ -5,14 +5,14 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
bool
|
<bool as FromParam<'a>>
|
||||||
isize
|
<isize as FromParam<'a>>
|
||||||
i8
|
<i8 as FromParam<'a>>
|
||||||
i16
|
<i16 as FromParam<'a>>
|
||||||
i32
|
<i32 as FromParam<'a>>
|
||||||
i64
|
<i64 as FromParam<'a>>
|
||||||
i128
|
<i128 as FromParam<'a>>
|
||||||
usize
|
<usize as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Q: FromSegments<'_>` is not satisfied
|
error[E0277]: the trait bound `Q: FromSegments<'_>` is not satisfied
|
||||||
|
@ -104,14 +104,14 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
bool
|
<bool as FromParam<'a>>
|
||||||
isize
|
<isize as FromParam<'a>>
|
||||||
i8
|
<i8 as FromParam<'a>>
|
||||||
i16
|
<i16 as FromParam<'a>>
|
||||||
i32
|
<i32 as FromParam<'a>>
|
||||||
i64
|
<i64 as FromParam<'a>>
|
||||||
i128
|
<i128 as FromParam<'a>>
|
||||||
usize
|
<usize as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied
|
error[E0277]: the trait bound `Q: FromRequest<'_>` is not satisfied
|
||||||
|
@ -138,14 +138,14 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
bool
|
<bool as FromParam<'a>>
|
||||||
isize
|
<isize as FromParam<'a>>
|
||||||
i8
|
<i8 as FromParam<'a>>
|
||||||
i16
|
<i16 as FromParam<'a>>
|
||||||
i32
|
<i32 as FromParam<'a>>
|
||||||
i64
|
<i64 as FromParam<'a>>
|
||||||
i128
|
<i128 as FromParam<'a>>
|
||||||
usize
|
<usize as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
||||||
error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
|
@ -155,12 +155,12 @@ error[E0277]: the trait bound `Q: FromParam<'_>` is not satisfied
|
||||||
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
| ^ the trait `FromParam<'_>` is not implemented for `Q`
|
||||||
|
|
|
|
||||||
= help: the following other types implement trait `FromParam<'a>`:
|
= help: the following other types implement trait `FromParam<'a>`:
|
||||||
bool
|
<bool as FromParam<'a>>
|
||||||
isize
|
<isize as FromParam<'a>>
|
||||||
i8
|
<i8 as FromParam<'a>>
|
||||||
i16
|
<i16 as FromParam<'a>>
|
||||||
i32
|
<i32 as FromParam<'a>>
|
||||||
i64
|
<i64 as FromParam<'a>>
|
||||||
i128
|
<i128 as FromParam<'a>>
|
||||||
usize
|
<usize as FromParam<'a>>
|
||||||
and $N others
|
and $N others
|
||||||
|
|
|
@ -86,7 +86,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Path, _>`
|
||||||
<i8 as FromUriParam<P, i8>>
|
<i8 as FromUriParam<P, i8>>
|
||||||
<i8 as FromUriParam<P, &'x i8>>
|
<i8 as FromUriParam<P, &'x i8>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:24:18
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:24:18
|
||||||
|
@ -190,7 +190,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>
|
||||||
<i8 as FromUriParam<P, i8>>
|
<i8 as FromUriParam<P, i8>>
|
||||||
<i8 as FromUriParam<P, &'x i8>>
|
<i8 as FromUriParam<P, &'x i8>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>` is not satisfied
|
error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:41:29
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:41:29
|
||||||
|
@ -229,7 +229,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>
|
||||||
<i8 as FromUriParam<P, i8>>
|
<i8 as FromUriParam<P, i8>>
|
||||||
<i8 as FromUriParam<P, &'x i8>>
|
<i8 as FromUriParam<P, &'x i8>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `S: Ignorable<rocket::http::uri::fmt::Query>` is not satisfied
|
error[E0277]: the trait bound `S: Ignorable<rocket::http::uri::fmt::Query>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:68:25
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:68:25
|
||||||
|
@ -298,7 +298,7 @@ error[E0277]: the trait bound `S: FromUriParam<rocket::http::uri::fmt::Query, _>
|
||||||
<i8 as FromUriParam<P, i8>>
|
<i8 as FromUriParam<P, i8>>
|
||||||
<i8 as FromUriParam<P, &'x i8>>
|
<i8 as FromUriParam<P, &'x i8>>
|
||||||
and $N others
|
and $N others
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:15:15
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:15:15
|
||||||
|
@ -450,7 +450,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:49:17
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:49:17
|
||||||
|
@ -462,7 +462,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, i64>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, i64>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:51:22
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:51:22
|
||||||
|
@ -474,7 +474,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:58:25
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:58:25
|
||||||
|
@ -487,7 +487,7 @@ error[E0277]: the trait bound `i32: FromUriParam<rocket::http::uri::fmt::Path, s
|
||||||
<i32 as FromUriParam<P, &'x i32>>
|
<i32 as FromUriParam<P, &'x i32>>
|
||||||
<i32 as FromUriParam<P, &'x mut i32>>
|
<i32 as FromUriParam<P, &'x mut i32>>
|
||||||
= note: required for `std::option::Option<i32>` to implement `FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>`
|
= note: required for `std::option::Option<i32>` to implement `FromUriParam<rocket::http::uri::fmt::Path, std::option::Option<{integer}>>`
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:60:19
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:60:19
|
||||||
|
@ -499,7 +499,7 @@ error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query
|
||||||
<isize as FromUriParam<P, isize>>
|
<isize as FromUriParam<P, isize>>
|
||||||
<isize as FromUriParam<P, &'x isize>>
|
<isize as FromUriParam<P, &'x isize>>
|
||||||
<isize as FromUriParam<P, &'x mut isize>>
|
<isize as FromUriParam<P, &'x mut isize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:62:24
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:62:24
|
||||||
|
@ -511,7 +511,7 @@ error[E0277]: the trait bound `isize: FromUriParam<rocket::http::uri::fmt::Query
|
||||||
<isize as FromUriParam<P, isize>>
|
<isize as FromUriParam<P, isize>>
|
||||||
<isize as FromUriParam<P, &'x isize>>
|
<isize as FromUriParam<P, &'x isize>>
|
||||||
<isize as FromUriParam<P, &'x mut isize>>
|
<isize as FromUriParam<P, &'x mut isize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:79:40
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:79:40
|
||||||
|
@ -523,7 +523,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:80:33
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:80:33
|
||||||
|
@ -535,7 +535,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:83:25
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:83:25
|
||||||
|
@ -547,7 +547,7 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path, &str>` is not satisfied
|
||||||
--> tests/ui-fail-stable/typed-uri-bad-type.rs:84:25
|
--> tests/ui-fail-stable/typed-uri-bad-type.rs:84:25
|
||||||
|
@ -559,4 +559,4 @@ error[E0277]: the trait bound `usize: FromUriParam<rocket::http::uri::fmt::Path,
|
||||||
<usize as FromUriParam<P, usize>>
|
<usize as FromUriParam<P, usize>>
|
||||||
<usize as FromUriParam<P, &'x usize>>
|
<usize as FromUriParam<P, &'x usize>>
|
||||||
<usize as FromUriParam<P, &'x mut usize>>
|
<usize as FromUriParam<P, &'x mut usize>>
|
||||||
= note: this error originates in the macro `rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `::rocket::rocket_internal_uri` which comes from the expansion of the macro `uri` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
|
|
||||||
use crate::uri::fmt::UriDisplay;
|
use crate::uri::fmt::UriDisplay;
|
||||||
use crate::uri::fmt::{self, Part};
|
use crate::uri::fmt::{self, Part};
|
||||||
|
@ -61,7 +63,7 @@ use crate::uri::fmt::{self, Part};
|
||||||
///
|
///
|
||||||
/// * `String`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`,
|
/// * `String`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`,
|
||||||
/// `u32`, `u64`, `u128`, `usize`, `f32`, `f64`, `bool`, `IpAddr`,
|
/// `u32`, `u64`, `u128`, `usize`, `f32`, `f64`, `bool`, `IpAddr`,
|
||||||
/// `Ipv4Addr`, `Ipv6Addr`, `&str`, `Cow<str>`
|
/// `Ipv4Addr`, `Ipv6Addr`, `&str`, `Cow<str>`, `Either<A, B>`
|
||||||
///
|
///
|
||||||
/// The following types have _identity_ implementations _only in [`Path`]_:
|
/// The following types have _identity_ implementations _only in [`Path`]_:
|
||||||
///
|
///
|
||||||
|
@ -375,7 +377,9 @@ impl<A, T: FromUriParam<fmt::Path, A>> FromUriParam<fmt::Path, A> for Option<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A no cost conversion allowing `T` to be used in place of an `Result<T, E>`.
|
/// A no cost conversion allowing `T` to be used in place of an `Result<T, E>`.
|
||||||
impl<A, E, T: FromUriParam<fmt::Path, A>> FromUriParam<fmt::Path, A> for Result<T, E> {
|
impl<A, E, T> FromUriParam<fmt::Path, A> for Result<T, E>
|
||||||
|
where T: FromUriParam<fmt::Path, A>
|
||||||
|
{
|
||||||
type Target = T::Target;
|
type Target = T::Target;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -384,6 +388,19 @@ impl<A, E, T: FromUriParam<fmt::Path, A>> FromUriParam<fmt::Path, A> for Result<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: Part, A, B, T, U> FromUriParam<P, Either<A, B>> for Either<T, U>
|
||||||
|
where T: FromUriParam<P, A>, U: FromUriParam<P, B>
|
||||||
|
{
|
||||||
|
type Target = Either<T::Target, U::Target>;
|
||||||
|
|
||||||
|
fn from_uri_param(param: Either<A, B>) -> Self::Target {
|
||||||
|
match param {
|
||||||
|
Either::Left(a) => Either::Left(T::from_uri_param(a)),
|
||||||
|
Either::Right(b) => Either::Right(U::from_uri_param(b)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<A, T: FromUriParam<fmt::Query, A>> FromUriParam<fmt::Query, Option<A>> for Option<T> {
|
impl<A, T: FromUriParam<fmt::Query, A>> FromUriParam<fmt::Query, Option<A>> for Option<T> {
|
||||||
type Target = Option<T::Target>;
|
type Target = Option<T::Target>;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::collections::{BTreeMap, HashMap};
|
||||||
use std::{fmt, path};
|
use std::{fmt, path};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
use time::{macros::format_description, format_description::FormatItem};
|
use time::{macros::format_description, format_description::FormatItem};
|
||||||
|
|
||||||
use crate::RawStr;
|
use crate::RawStr;
|
||||||
|
@ -421,6 +422,17 @@ impl<P: Part, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defers to `T` or `U` in `Either<T, U>`.
|
||||||
|
impl<P: Part, T: UriDisplay<P>, U: UriDisplay<P>> UriDisplay<P> for Either<T, U> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_, P>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Either::Left(t) => UriDisplay::fmt(t, f),
|
||||||
|
Either::Right(u) => UriDisplay::fmt(u, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Defers to the `UriDisplay<P>` implementation for `T`.
|
/// Defers to the `UriDisplay<P>` implementation for `T`.
|
||||||
impl<P: Part, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &mut T {
|
impl<P: Part, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &mut T {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -185,7 +185,7 @@ type TestData<'a> = (
|
||||||
fn fuzz((route_a, route_b, req): TestData<'_>) {
|
fn fuzz((route_a, route_b, req): TestData<'_>) {
|
||||||
let rocket = rocket::custom(rocket::Config {
|
let rocket = rocket::custom(rocket::Config {
|
||||||
workers: 2,
|
workers: 2,
|
||||||
// log_level: rocket::log::LogLevel::Off,
|
log_level: None,
|
||||||
cli_colors: rocket::config::CliColors::Never,
|
cli_colors: rocket::config::CliColors::Never,
|
||||||
..rocket::Config::debug_default()
|
..rocket::Config::debug_default()
|
||||||
});
|
});
|
||||||
|
|
|
@ -127,6 +127,9 @@ pub struct Catcher {
|
||||||
///
|
///
|
||||||
/// This is -(number of nonempty segments in base).
|
/// This is -(number of nonempty segments in base).
|
||||||
pub(crate) rank: isize,
|
pub(crate) rank: isize,
|
||||||
|
|
||||||
|
/// The catcher's file, line, and column location.
|
||||||
|
pub(crate) location: Option<(&'static str, u32, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rank is computed as -(number of nonempty segments in base) => catchers
|
// The rank is computed as -(number of nonempty segments in base) => catchers
|
||||||
|
@ -185,7 +188,8 @@ impl Catcher {
|
||||||
base: uri::Origin::root().clone(),
|
base: uri::Origin::root().clone(),
|
||||||
handler: Box::new(handler),
|
handler: Box::new(handler),
|
||||||
rank: rank(uri::Origin::root().path()),
|
rank: rank(uri::Origin::root().path()),
|
||||||
code
|
code,
|
||||||
|
location: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,6 +332,8 @@ pub struct StaticInfo {
|
||||||
pub code: Option<u16>,
|
pub code: Option<u16>,
|
||||||
/// The catcher's handler, i.e, the annotated function.
|
/// The catcher's handler, i.e, the annotated function.
|
||||||
pub handler: for<'r> fn(Status, &'r Request<'_>) -> BoxFuture<'r>,
|
pub handler: for<'r> fn(Status, &'r Request<'_>) -> BoxFuture<'r>,
|
||||||
|
/// The file, line, and column where the catcher was defined.
|
||||||
|
pub location: (&'static str, u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -336,6 +342,7 @@ impl From<StaticInfo> for Catcher {
|
||||||
fn from(info: StaticInfo) -> Catcher {
|
fn from(info: StaticInfo) -> Catcher {
|
||||||
let mut catcher = Catcher::new(info.code, info.handler);
|
let mut catcher = Catcher::new(info.code, info.handler);
|
||||||
catcher.name = Some(info.name.into());
|
catcher.name = Some(info.name.into());
|
||||||
|
catcher.location = Some(info.location);
|
||||||
catcher
|
catcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,13 @@ use figment::{Figment, Profile, Provider, Metadata, error::Result};
|
||||||
use figment::providers::{Serialized, Env, Toml, Format};
|
use figment::providers::{Serialized, Env, Toml, Format};
|
||||||
use figment::value::{Map, Dict, magic::RelativePathBuf};
|
use figment::value::{Map, Dict, magic::RelativePathBuf};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::Level;
|
|
||||||
|
|
||||||
#[cfg(feature = "secrets")]
|
#[cfg(feature = "secrets")]
|
||||||
use crate::config::SecretKey;
|
use crate::config::SecretKey;
|
||||||
use crate::config::{ShutdownConfig, Ident, CliColors};
|
use crate::config::{ShutdownConfig, Level, TraceFormat, Ident, CliColors};
|
||||||
use crate::request::{self, Request, FromRequest};
|
use crate::request::{self, Request, FromRequest};
|
||||||
use crate::http::uncased::Uncased;
|
use crate::http::uncased::Uncased;
|
||||||
use crate::data::Limits;
|
use crate::data::Limits;
|
||||||
use crate::trace::{Trace, TraceFormat};
|
|
||||||
|
|
||||||
/// Rocket server configuration.
|
/// Rocket server configuration.
|
||||||
///
|
///
|
||||||
|
@ -288,7 +286,7 @@ impl Config {
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If extraction fails, prints an error message indicating the error and
|
/// If extraction fails, logs an error message indicating the error and
|
||||||
/// panics. For a version that doesn't panic, use [`Config::try_from()`].
|
/// panics. For a version that doesn't panic, use [`Config::try_from()`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -306,7 +304,12 @@ impl Config {
|
||||||
/// let config = Config::from(figment);
|
/// let config = Config::from(figment);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from<T: Provider>(provider: T) -> Self {
|
pub fn from<T: Provider>(provider: T) -> Self {
|
||||||
Self::try_from(provider).unwrap_or_else(bail_with_config_error)
|
use crate::trace::Trace;
|
||||||
|
|
||||||
|
Self::try_from(provider).unwrap_or_else(|e| {
|
||||||
|
e.trace_error();
|
||||||
|
panic!("aborting due to configuration error(s)")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,15 +436,3 @@ impl<'r> FromRequest<'r> for &'r Config {
|
||||||
request::Outcome::Success(req.rocket().config())
|
request::Outcome::Success(req.rocket().config())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn bail_with_config_error<T>(error: figment::Error) -> T {
|
|
||||||
pretty_print_error(error);
|
|
||||||
panic!("aborting due to configuration error(s)")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
// FIXME: Remove this function.
|
|
||||||
pub fn pretty_print_error(error: figment::Error) {
|
|
||||||
error.trace_error()
|
|
||||||
}
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ pub use ident::Ident;
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
pub use cli_colors::CliColors;
|
pub use cli_colors::CliColors;
|
||||||
|
|
||||||
// pub use crate::log::LogLevel;
|
pub use crate::trace::{TraceFormat, Level};
|
||||||
pub use crate::shutdown::ShutdownConfig;
|
pub use crate::shutdown::ShutdownConfig;
|
||||||
|
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
|
@ -139,6 +139,3 @@ pub use crate::shutdown::Sig;
|
||||||
|
|
||||||
#[cfg(feature = "secrets")]
|
#[cfg(feature = "secrets")]
|
||||||
pub use secret_key::SecretKey;
|
pub use secret_key::SecretKey;
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use config::{pretty_print_error, bail_with_config_error};
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ use std::sync::Arc;
|
||||||
use figment::Profile;
|
use figment::Profile;
|
||||||
|
|
||||||
use crate::listener::Endpoint;
|
use crate::listener::Endpoint;
|
||||||
use crate::trace::Trace;
|
|
||||||
use crate::{Ignite, Orbit, Phase, Rocket};
|
use crate::{Ignite, Orbit, Phase, Rocket};
|
||||||
|
use crate::trace::Trace;
|
||||||
|
|
||||||
/// An error that occurs during launch.
|
/// An error that occurs during launch.
|
||||||
///
|
///
|
||||||
|
@ -84,7 +84,7 @@ impl Error {
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => process::ExitCode::SUCCESS,
|
Ok(_) => process::ExitCode::SUCCESS,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error_span!("aborting launch due to error" => e.trace_error());
|
span_error!("error", "aborting launch due to error" => e.trace_error());
|
||||||
process::ExitCode::SUCCESS
|
process::ExitCode::SUCCESS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,14 +200,14 @@ impl fmt::Display for ServerError<'_> {
|
||||||
pub(crate) fn log_server_error(error: &(dyn StdError + 'static)) {
|
pub(crate) fn log_server_error(error: &(dyn StdError + 'static)) {
|
||||||
let mut error: &(dyn StdError + 'static) = error;
|
let mut error: &(dyn StdError + 'static) = error;
|
||||||
if error.downcast_ref::<hyper::Error>().is_some() {
|
if error.downcast_ref::<hyper::Error>().is_some() {
|
||||||
warn_span!("minor server error" ["{}", ServerError(error)] => {
|
span_warn!("request error", "{}", ServerError(error) => {
|
||||||
while let Some(source) = error.source() {
|
while let Some(source) = error.source() {
|
||||||
error = source;
|
error = source;
|
||||||
warn!("{}", ServerError(error));
|
warn!("{}", ServerError(error));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
error_span!("server error" ["{}", ServerError(error)] => {
|
span_error!("server error", "{}", ServerError(error) => {
|
||||||
while let Some(source) = error.source() {
|
while let Some(source) = error.source() {
|
||||||
error = source;
|
error = source;
|
||||||
error!("{}", ServerError(error));
|
error!("{}", ServerError(error));
|
||||||
|
@ -215,3 +215,59 @@ pub(crate) fn log_server_error(error: &(dyn StdError + 'static)) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod display_hack_impl {
|
||||||
|
use super::*;
|
||||||
|
use crate::util::Formatter;
|
||||||
|
|
||||||
|
/// The *magic*.
|
||||||
|
///
|
||||||
|
/// This type implements a `display()` method using an internal `T` that is
|
||||||
|
/// either `fmt::Display` _or_ `fmt::Debug`, using the former when
|
||||||
|
/// available. It does so by using a "specialization" hack: it has a blanket
|
||||||
|
/// DefaultDisplay trait impl for all types that are `fmt::Debug` and a
|
||||||
|
/// "specialized" inherent impl for all types that are `fmt::Display`.
|
||||||
|
///
|
||||||
|
/// As long as `T: Display`, the "specialized" impl is what Rust will
|
||||||
|
/// resolve `DisplayHack(v).display()` to when `T: fmt::Display` as it is an
|
||||||
|
/// inherent impl. Otherwise, Rust will fall back to the blanket impl.
|
||||||
|
pub struct DisplayHack<T: ?Sized>(pub T);
|
||||||
|
|
||||||
|
pub trait DefaultDisplay {
|
||||||
|
fn display(&self) -> impl fmt::Display;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blanket implementation for `T: Debug`. This is what Rust will resolve
|
||||||
|
/// `DisplayHack<T>::display` to when `T: Debug`.
|
||||||
|
impl<T: fmt::Debug + ?Sized> DefaultDisplay for DisplayHack<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn display(&self) -> impl fmt::Display {
|
||||||
|
Formatter(|f| fmt::Debug::fmt(&self.0, f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "Specialized" implementation for `T: Display`. This is what Rust will
|
||||||
|
/// resolve `DisplayHack<T>::display` to when `T: Display`.
|
||||||
|
impl<T: fmt::Display + fmt::Debug + ?Sized> DisplayHack<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn display(&self) -> impl fmt::Display + '_ {
|
||||||
|
Formatter(|f| fmt::Display::fmt(&self.0, f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! display_hack {
|
||||||
|
($v:expr) => ({
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use $crate::error::display_hack_impl::{DisplayHack, DefaultDisplay as _};
|
||||||
|
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
DisplayHack($v).display()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use display_hack as display_hack;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use futures::future::{Future, BoxFuture, FutureExt};
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use futures::future::{Future, BoxFuture, FutureExt};
|
||||||
|
|
||||||
use crate::route::RouteUri;
|
|
||||||
use crate::{Rocket, Request, Response, Data, Build, Orbit};
|
use crate::{Rocket, Request, Response, Data, Build, Orbit};
|
||||||
use crate::fairing::{Fairing, Kind, Info, Result};
|
use crate::fairing::{Fairing, Kind, Info, Result};
|
||||||
|
use crate::route::RouteUri;
|
||||||
|
use crate::trace::Trace;
|
||||||
|
|
||||||
/// A ad-hoc fairing that can be created from a function or closure.
|
/// A ad-hoc fairing that can be created from a function or closure.
|
||||||
///
|
///
|
||||||
|
@ -235,7 +236,7 @@ impl AdHoc {
|
||||||
let app_config = match rocket.figment().extract::<T>() {
|
let app_config = match rocket.figment().extract::<T>() {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
crate::config::pretty_print_error(e);
|
e.trace_error();
|
||||||
return Err(rocket);
|
return Err(rocket);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,16 +51,6 @@ impl Fairings {
|
||||||
iter!(self, self.active().collect::<HashSet<_>>().into_iter())
|
iter!(self, self.active().collect::<HashSet<_>>().into_iter())
|
||||||
.map(|v| v.1)
|
.map(|v| v.1)
|
||||||
.collect()
|
.collect()
|
||||||
// .into_iter()
|
|
||||||
// .map(|i| )
|
|
||||||
// if !active_fairings.is_empty() {
|
|
||||||
// tracing::info_span!("fairings").in_scope(|| {
|
|
||||||
// for (_, fairing) in iter!(self, active_fairings.into_iter()) {
|
|
||||||
// let (name, kind) = (fairing.info().name, fairing.info().kind);
|
|
||||||
// info!(name: "fairing", name, %kind)
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, fairing: Box<dyn Fairing>) {
|
pub fn add(&mut self, fairing: Box<dyn Fairing>) {
|
||||||
|
|
|
@ -123,6 +123,7 @@ pub use tokio;
|
||||||
pub use figment;
|
pub use figment;
|
||||||
pub use time;
|
pub use time;
|
||||||
pub use tracing;
|
pub use tracing;
|
||||||
|
pub use either;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
|
@ -164,8 +165,6 @@ mod router;
|
||||||
mod phase;
|
mod phase;
|
||||||
mod erased;
|
mod erased;
|
||||||
|
|
||||||
#[doc(hidden)] pub use either::Either;
|
|
||||||
|
|
||||||
#[doc(inline)] pub use rocket_codegen::*;
|
#[doc(inline)] pub use rocket_codegen::*;
|
||||||
|
|
||||||
#[doc(inline)] pub use crate::response::Response;
|
#[doc(inline)] pub use crate::response::Response;
|
||||||
|
@ -254,6 +253,11 @@ pub fn async_test<R>(fut: impl std::future::Future<Output = R>) -> R {
|
||||||
/// WARNING: This is unstable! Do not use this method outside of Rocket!
|
/// WARNING: This is unstable! Do not use this method outside of Rocket!
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn async_main<R>(fut: impl std::future::Future<Output = R> + Send) -> R {
|
pub fn async_main<R>(fut: impl std::future::Future<Output = R> + Send) -> R {
|
||||||
|
fn bail<T, E: crate::trace::Trace>(e: E) -> T {
|
||||||
|
e.trace_error();
|
||||||
|
panic!("aborting due to error")
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: We need to run `fut` to get the user's `Figment` to properly set
|
// FIXME: We need to run `fut` to get the user's `Figment` to properly set
|
||||||
// up the async env, but we need the async env to run `fut`. So we're stuck.
|
// up the async env, but we need the async env to run `fut`. So we're stuck.
|
||||||
// Tokio doesn't let us take the state from one async env and migrate it to
|
// Tokio doesn't let us take the state from one async env and migrate it to
|
||||||
|
@ -263,8 +267,6 @@ pub fn async_main<R>(fut: impl std::future::Future<Output = R> + Send) -> R {
|
||||||
// values won't reflect swaps of `Rocket` in attach fairings with different
|
// values won't reflect swaps of `Rocket` in attach fairings with different
|
||||||
// config values, or values from non-Rocket configs. See tokio-rs/tokio#3329
|
// config values, or values from non-Rocket configs. See tokio-rs/tokio#3329
|
||||||
// for a necessary resolution in `tokio`.
|
// for a necessary resolution in `tokio`.
|
||||||
use config::bail_with_config_error as bail;
|
|
||||||
|
|
||||||
let fig = Config::figment();
|
let fig = Config::figment();
|
||||||
let workers = fig.extract_inner(Config::WORKERS).unwrap_or_else(bail);
|
let workers = fig.extract_inner(Config::WORKERS).unwrap_or_else(bail);
|
||||||
let max_blocking = fig.extract_inner(Config::MAX_BLOCKING).unwrap_or_else(bail);
|
let max_blocking = fig.extract_inner(Config::MAX_BLOCKING).unwrap_or_else(bail);
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use futures::future::{FutureExt, Future};
|
use futures::future::{FutureExt, Future};
|
||||||
|
|
||||||
use crate::{route, catcher, Rocket, Orbit, Request, Response, Data};
|
|
||||||
use crate::trace::Trace;
|
use crate::trace::Trace;
|
||||||
use crate::util::Formatter;
|
use crate::util::Formatter;
|
||||||
use crate::data::IoHandler;
|
use crate::data::IoHandler;
|
||||||
use crate::http::{Method, Status, Header};
|
use crate::http::{Method, Status, Header};
|
||||||
use crate::outcome::Outcome;
|
use crate::outcome::Outcome;
|
||||||
use crate::form::Form;
|
use crate::form::Form;
|
||||||
|
use crate::{route, catcher, Rocket, Orbit, Request, Response, Data};
|
||||||
|
|
||||||
// A token returned to force the execution of one method before another.
|
// A token returned to force the execution of one method before another.
|
||||||
pub(crate) struct RequestToken;
|
pub(crate) struct RequestToken;
|
||||||
|
@ -199,6 +199,7 @@ impl Rocket<Orbit> {
|
||||||
let mut status = Status::NotFound;
|
let mut status = Status::NotFound;
|
||||||
for route in self.router.route(request) {
|
for route in self.router.route(request) {
|
||||||
// Retrieve and set the requests parameters.
|
// Retrieve and set the requests parameters.
|
||||||
|
route.trace_info();
|
||||||
request.set_route(route);
|
request.set_route(route);
|
||||||
|
|
||||||
let name = route.name.as_deref();
|
let name = route.name.as_deref();
|
||||||
|
@ -207,7 +208,6 @@ impl Rocket<Orbit> {
|
||||||
|
|
||||||
// Check if the request processing completed (Some) or if the
|
// Check if the request processing completed (Some) or if the
|
||||||
// request needs to be forwarded. If it does, continue the loop
|
// request needs to be forwarded. If it does, continue the loop
|
||||||
route.trace_info();
|
|
||||||
outcome.trace_info();
|
outcome.trace_info();
|
||||||
match outcome {
|
match outcome {
|
||||||
o@Outcome::Success(_) | o@Outcome::Error(_) => return o,
|
o@Outcome::Success(_) | o@Outcome::Error(_) => return o,
|
||||||
|
@ -215,9 +215,7 @@ impl Rocket<Orbit> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let outcome = Outcome::Forward((data, status));
|
Outcome::Forward((data, status))
|
||||||
outcome.trace_info();
|
|
||||||
outcome
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invokes the catcher for `status`. Returns the response on success.
|
// Invokes the catcher for `status`. Returns the response on success.
|
||||||
|
|
|
@ -138,7 +138,7 @@ macro_rules! pub_client_impl {
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
|
||||||
let figment = rocket.figment().clone()
|
let figment = rocket.figment().clone()
|
||||||
// .merge((config::Config::LOG_LEVEL, config::LogLevel::Debug))
|
.merge((config::Config::LOG_LEVEL, "debug"))
|
||||||
.select(config::Config::DEBUG_PROFILE);
|
.select(config::Config::DEBUG_PROFILE);
|
||||||
|
|
||||||
Self::tracked(rocket.reconfigure(figment)) $(.$suffix)?
|
Self::tracked(rocket.reconfigure(figment)) $(.$suffix)?
|
||||||
|
|
|
@ -2,6 +2,7 @@ use std::str::FromStr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::error::Empty;
|
use crate::error::Empty;
|
||||||
|
use crate::either::Either;
|
||||||
use crate::http::uri::{Segments, error::PathError, fmt::Path};
|
use crate::http::uri::{Segments, error::PathError, fmt::Path};
|
||||||
|
|
||||||
/// Trait to convert a dynamic path segment string to a concrete value.
|
/// Trait to convert a dynamic path segment string to a concrete value.
|
||||||
|
@ -40,29 +41,56 @@ use crate::http::uri::{Segments, error::PathError, fmt::Path};
|
||||||
///
|
///
|
||||||
/// Sometimes, a forward is not desired, and instead, we simply want to know
|
/// Sometimes, a forward is not desired, and instead, we simply want to know
|
||||||
/// that the dynamic path segment could not be parsed into some desired type
|
/// that the dynamic path segment could not be parsed into some desired type
|
||||||
/// `T`. In these cases, types of `Option<T>` or `Result<T, T::Error>` can be
|
/// `T`. In these cases, types of `Option<T>`, `Result<T, T::Error>`, or
|
||||||
/// used. These types implement `FromParam` themselves. Their implementations
|
/// `Either<A, B>` can be used, which implement `FromParam` themselves.
|
||||||
/// always return successfully, so they never forward. They can be used to
|
|
||||||
/// determine if the `FromParam` call failed and to retrieve the error value
|
|
||||||
/// from the failed `from_param` call.
|
|
||||||
///
|
///
|
||||||
/// For instance, imagine you've asked for an `<id>` as a `usize`. To determine
|
/// * **`Option<T>`** _where_ **`T: FromParam`**
|
||||||
/// when the `<id>` was not a valid `usize` and retrieve the string that failed
|
///
|
||||||
/// to parse, you can use a `Result<usize, &str>` type for the `<id>` parameter
|
/// Always returns successfully.
|
||||||
/// as follows:
|
///
|
||||||
|
/// If the conversion to `T` fails, `None` is returned. If the conversion
|
||||||
|
/// succeeds, `Some(value)` is returned.
|
||||||
|
///
|
||||||
|
/// * **`Result<T, T::Error>`** _where_ **`T: FromParam`**
|
||||||
|
///
|
||||||
|
/// Always returns successfully.
|
||||||
|
///
|
||||||
|
/// If the conversion to `T` fails, `Err(error)` is returned. If the
|
||||||
|
/// conversion succeeds, `Ok(value)` is returned.
|
||||||
|
///
|
||||||
|
/// * **`Either<A, B>`** _where_ **`A: FromParam`** _and_ **`B: FromParam`**
|
||||||
|
///
|
||||||
|
/// Fails only when both `A::from_param` and `B::from_param` fail. If one
|
||||||
|
/// of the two succeeds, the successful value is returned in
|
||||||
|
/// `Either::Left(A)` or `Either::Right(B)` variant, respectively. If both
|
||||||
|
/// fail, the error values from both calls are returned in a tuple in the
|
||||||
|
/// `Err` variant.
|
||||||
|
///
|
||||||
|
/// `Either<A, B>` is particularly useful with a `B` type of `&str`, allowing
|
||||||
|
/// you to retrieve the invalid path segment. Because `&str`'s implementation of
|
||||||
|
/// `FromParam` always succeeds, the `Right` variant of the `Either` will always
|
||||||
|
/// contain the path segment in case of failure.
|
||||||
|
///
|
||||||
|
/// For instance, consider the following route and handler:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #[macro_use] extern crate rocket;
|
/// # #[macro_use] extern crate rocket;
|
||||||
|
/// use rocket::either::{Either, Left, Right};
|
||||||
|
///
|
||||||
/// #[get("/<id>")]
|
/// #[get("/<id>")]
|
||||||
/// fn hello(id: Result<usize, &str>) -> String {
|
/// fn hello(id: Either<usize, &str>) -> String {
|
||||||
/// match id {
|
/// match id {
|
||||||
/// Ok(id_num) => format!("usize: {}", id_num),
|
/// Left(id_num) => format!("usize: {}", id_num),
|
||||||
/// Err(string) => format!("Not a usize: {}", string)
|
/// Right(string) => format!("Not a usize: {}", string)
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// # fn main() { }
|
/// # fn main() { }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// In the above example, if the dynamic path segment cannot be parsed into a
|
||||||
|
/// `usize`, the raw path segment is returned in the `Right` variant of the
|
||||||
|
/// `Either<usize, &str>` value.
|
||||||
|
///
|
||||||
/// # Provided Implementations
|
/// # Provided Implementations
|
||||||
///
|
///
|
||||||
/// Rocket implements `FromParam` for several standard library types. Their
|
/// Rocket implements `FromParam` for several standard library types. Their
|
||||||
|
@ -219,11 +247,11 @@ impl<'a> FromParam<'a> for String {
|
||||||
macro_rules! impl_with_fromstr {
|
macro_rules! impl_with_fromstr {
|
||||||
($($T:ty),+) => ($(
|
($($T:ty),+) => ($(
|
||||||
impl<'a> FromParam<'a> for $T {
|
impl<'a> FromParam<'a> for $T {
|
||||||
type Error = &'a str;
|
type Error = <$T as FromStr>::Err;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_param(param: &'a str) -> Result<Self, Self::Error> {
|
fn from_param(param: &'a str) -> Result<Self, Self::Error> {
|
||||||
<$T as FromStr>::from_str(param).map_err(|_| param)
|
<$T as FromStr>::from_str(param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+)
|
)+)
|
||||||
|
@ -361,3 +389,23 @@ impl<'r, T: FromSegments<'r>> FromSegments<'r> for Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements `FromParam` for `Either<A, B>`, where `A` and `B` both implement
|
||||||
|
/// `FromParam`. If `A::from_param` returns `Ok(a)`, `Either::Left(a)` is
|
||||||
|
/// returned. If `B::from_param` returns `Ok(b)`, `Either::Right(b)` is
|
||||||
|
/// returned. If both `A::from_param` and `B::from_param` return `Err(a)` and
|
||||||
|
/// `Err(b)`, respectively, then `Err((a, b))` is returned.
|
||||||
|
impl<'v, A: FromParam<'v>, B: FromParam<'v>> FromParam<'v> for Either<A, B> {
|
||||||
|
type Error = (A::Error, B::Error);
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn from_param(param: &'v str) -> Result<Self, Self::Error> {
|
||||||
|
match A::from_param(param) {
|
||||||
|
Ok(a) => Ok(Either::Left(a)),
|
||||||
|
Err(a) => match B::from_param(param) {
|
||||||
|
Ok(b) => Ok(Either::Right(b)),
|
||||||
|
Err(b) => Err((a, b)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -484,7 +484,8 @@ impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Option<R> {
|
||||||
match self {
|
match self {
|
||||||
Some(r) => r.respond_to(req),
|
Some(r) => r.respond_to(req),
|
||||||
None => {
|
None => {
|
||||||
debug!("{} responder returned `None`", std::any::type_name::<Self>());
|
let type_name = std::any::type_name::<Self>();
|
||||||
|
debug!(type_name, "`Option` responder returned `None`");
|
||||||
Err(Status::NotFound)
|
Err(Status::NotFound)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -506,13 +507,13 @@ impl<'r, 'o: 'r, 't: 'o, 'e: 'o, T, E> Responder<'r, 'o> for Result<T, E>
|
||||||
|
|
||||||
/// Responds with the wrapped `Responder` in `self`, whether it is `Left` or
|
/// Responds with the wrapped `Responder` in `self`, whether it is `Left` or
|
||||||
/// `Right`.
|
/// `Right`.
|
||||||
impl<'r, 'o: 'r, 't: 'o, 'e: 'o, T, E> Responder<'r, 'o> for crate::Either<T, E>
|
impl<'r, 'o: 'r, 't: 'o, 'e: 'o, T, E> Responder<'r, 'o> for either::Either<T, E>
|
||||||
where T: Responder<'r, 't>, E: Responder<'r, 'e>
|
where T: Responder<'r, 't>, E: Responder<'r, 'e>
|
||||||
{
|
{
|
||||||
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
|
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
|
||||||
match self {
|
match self {
|
||||||
crate::Either::Left(r) => r.respond_to(req),
|
either::Either::Left(r) => r.respond_to(req),
|
||||||
crate::Either::Right(r) => r.respond_to(req),
|
either::Either::Right(r) => r.respond_to(req),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,16 +185,9 @@ impl Rocket<Build> {
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn custom<T: Provider>(provider: T) -> Self {
|
pub fn custom<T: Provider>(provider: T) -> Self {
|
||||||
// We initialize the logger here so that logging from fairings and so on
|
Rocket::<Build>(Building::default())
|
||||||
// are visible; we use the final config to set a max log-level in ignite
|
.reconfigure(provider)
|
||||||
crate::trace::init(None);
|
.attach(Shield::default())
|
||||||
|
|
||||||
let rocket: Rocket<Build> = Rocket(Building {
|
|
||||||
figment: Figment::from(provider),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
rocket.attach(Shield::default())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overrides the current configuration provider with `provider`.
|
/// Overrides the current configuration provider with `provider`.
|
||||||
|
@ -237,7 +230,12 @@ impl Rocket<Build> {
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn reconfigure<T: Provider>(mut self, provider: T) -> Self {
|
pub fn reconfigure<T: Provider>(mut self, provider: T) -> Self {
|
||||||
|
// We initialize the logger here so that logging from fairings and so on
|
||||||
|
// are visible; we use the final config to set a max log-level in ignite
|
||||||
self.figment = Figment::from(provider);
|
self.figment = Figment::from(provider);
|
||||||
|
crate::trace::init(Config::try_from(&self.figment).ok().as_ref());
|
||||||
|
span_trace!("reconfigure" => self.figment().trace_trace());
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,14 +564,14 @@ impl Rocket<Build> {
|
||||||
// Log everything we know: config, routes, catchers, fairings.
|
// Log everything we know: config, routes, catchers, fairings.
|
||||||
// TODO: Store/print managed state type names?
|
// TODO: Store/print managed state type names?
|
||||||
let fairings = self.fairings.unique_set();
|
let fairings = self.fairings.unique_set();
|
||||||
info_span!("config" [profile = %self.figment().profile()] => {
|
span_info!("config", profile = %self.figment().profile() => {
|
||||||
config.trace_info();
|
config.trace_info();
|
||||||
self.figment().trace_debug();
|
self.figment().trace_debug();
|
||||||
});
|
});
|
||||||
|
|
||||||
info_span!("routes" [count = self.routes.len()] => self.routes().trace_all_info());
|
span_info!("routes", count = self.routes.len() => self.routes().trace_all_info());
|
||||||
info_span!("catchers" [count = self.catchers.len()] => self.catchers().trace_all_info());
|
span_info!("catchers", count = self.catchers.len() => self.catchers().trace_all_info());
|
||||||
info_span!("fairings" [count = fairings.len()] => fairings.trace_all_info());
|
span_info!("fairings", count = fairings.len() => fairings.trace_all_info());
|
||||||
|
|
||||||
// Ignite the rocket.
|
// Ignite the rocket.
|
||||||
let rocket: Rocket<Ignite> = Rocket(Igniting {
|
let rocket: Rocket<Ignite> = Rocket(Igniting {
|
||||||
|
@ -592,19 +590,6 @@ impl Rocket<Build> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "items", skip_all, fields(kind = kind))]
|
|
||||||
fn log_items<T, I, B, O>(kind: &str, items: I, base: B, origin: O)
|
|
||||||
where T: fmt::Display + Copy, I: Iterator<Item = T>,
|
|
||||||
B: Fn(&T) -> &Origin<'_>, O: Fn(&T) -> &Origin<'_>
|
|
||||||
{
|
|
||||||
let mut items: Vec<_> = items.collect();
|
|
||||||
items.sort_by_key(|i| origin(i).path().as_str().chars().count());
|
|
||||||
items.sort_by_key(|i| origin(i).path().segments().count());
|
|
||||||
items.sort_by_key(|i| base(i).path().as_str().chars().count());
|
|
||||||
items.sort_by_key(|i| base(i).path().segments().count());
|
|
||||||
items.iter().for_each(|item| info!(name: "item", %item));
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rocket<Ignite> {
|
impl Rocket<Ignite> {
|
||||||
/// Returns the finalized, active configuration. This is guaranteed to
|
/// Returns the finalized, active configuration. This is guaranteed to
|
||||||
/// remain stable through ignition and into orbit.
|
/// remain stable through ignition and into orbit.
|
||||||
|
|
|
@ -176,6 +176,8 @@ pub struct Route {
|
||||||
pub format: Option<MediaType>,
|
pub format: Option<MediaType>,
|
||||||
/// The discovered sentinels.
|
/// The discovered sentinels.
|
||||||
pub(crate) sentinels: Vec<Sentry>,
|
pub(crate) sentinels: Vec<Sentry>,
|
||||||
|
/// The file, line, and column where the route was defined, if known.
|
||||||
|
pub(crate) location: Option<(&'static str, u32, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Route {
|
impl Route {
|
||||||
|
@ -250,6 +252,7 @@ impl Route {
|
||||||
format: None,
|
format: None,
|
||||||
sentinels: Vec::new(),
|
sentinels: Vec::new(),
|
||||||
handler: Box::new(handler),
|
handler: Box::new(handler),
|
||||||
|
location: None,
|
||||||
rank, uri, method,
|
rank, uri, method,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,6 +374,8 @@ pub struct StaticInfo {
|
||||||
/// Route-derived sentinels, if any.
|
/// Route-derived sentinels, if any.
|
||||||
/// This isn't `&'static [SentryInfo]` because `type_name()` isn't `const`.
|
/// This isn't `&'static [SentryInfo]` because `type_name()` isn't `const`.
|
||||||
pub sentinels: Vec<Sentry>,
|
pub sentinels: Vec<Sentry>,
|
||||||
|
/// The file, line, and column where the route was defined.
|
||||||
|
pub location: (&'static str, u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -386,6 +391,7 @@ impl From<StaticInfo> for Route {
|
||||||
rank: info.rank.unwrap_or_else(|| uri.default_rank()),
|
rank: info.rank.unwrap_or_else(|| uri.default_rank()),
|
||||||
format: info.format,
|
format: info.format,
|
||||||
sentinels: info.sentinels.into_iter().collect(),
|
sentinels: info.sentinels.into_iter().collect(),
|
||||||
|
location: Some(info.location),
|
||||||
uri,
|
uri,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl Rocket<Orbit> {
|
||||||
Request::from_hyp(rocket, parts, connection).unwrap_or_else(|e| e)
|
Request::from_hyp(rocket, parts, connection).unwrap_or_else(|e| e)
|
||||||
});
|
});
|
||||||
|
|
||||||
debug_span!("request headers" => request.inner().headers().iter().trace_all_debug());
|
span_debug!("request headers" => request.inner().headers().iter().trace_all_debug());
|
||||||
let mut response = request.into_response(
|
let mut response = request.into_response(
|
||||||
stream,
|
stream,
|
||||||
|rocket, request, data| Box::pin(rocket.preprocess(request, data)),
|
|rocket, request, data| Box::pin(rocket.preprocess(request, data)),
|
||||||
|
@ -54,7 +54,7 @@ impl Rocket<Orbit> {
|
||||||
|
|
||||||
// TODO: Should upgrades be handled in dispatch?
|
// TODO: Should upgrades be handled in dispatch?
|
||||||
response.inner().trace_info();
|
response.inner().trace_info();
|
||||||
debug_span!("response headers" => response.inner().headers().iter().trace_all_debug());
|
span_debug!("response headers" => response.inner().headers().iter().trace_all_debug());
|
||||||
let io_handler = response.make_io_handler(Rocket::extract_io_handler);
|
let io_handler = response.make_io_handler(Rocket::extract_io_handler);
|
||||||
if let (Some((proto, handler)), Some(upgrade)) = (io_handler, upgrade) {
|
if let (Some((proto, handler)), Some(upgrade)) = (io_handler, upgrade) {
|
||||||
let upgrade = upgrade.map_ok(IoStream::from).map_err(io::Error::other);
|
let upgrade = upgrade.map_ok(IoStream::from).map_err(io::Error::other);
|
||||||
|
@ -92,7 +92,7 @@ async fn io_handler_task<S>(proto: String, stream: S, mut handler: ErasedIoHandl
|
||||||
Err(e) => return warn!(error = %e, "i/o upgrade failed"),
|
Err(e) => return warn!(error = %e, "i/o upgrade failed"),
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("i/o upgrade succeeded");
|
debug!("i/o upgrade succeeded");
|
||||||
if let Err(e) = handler.take().io(stream).await {
|
if let Err(e) = handler.take().io(stream).await {
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
io::ErrorKind::BrokenPipe => warn!("i/o handler closed"),
|
io::ErrorKind::BrokenPipe => warn!("i/o handler closed"),
|
||||||
|
|
|
@ -4,8 +4,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use crate::{Rocket, Request, Response, Orbit, Config};
|
use crate::{Rocket, Request, Response, Orbit, Config};
|
||||||
use crate::fairing::{Fairing, Info, Kind};
|
use crate::fairing::{Fairing, Info, Kind};
|
||||||
use crate::http::{Header, uncased::UncasedStr};
|
use crate::http::{Header, uncased::UncasedStr};
|
||||||
use crate::shield::*;
|
use crate::shield::{Frame, Hsts, NoSniff, Permission, Policy};
|
||||||
use crate::trace::*;
|
use crate::trace::{Trace, TraceAll};
|
||||||
|
|
||||||
/// A [`Fairing`] that injects browser security and privacy headers into all
|
/// A [`Fairing`] that injects browser security and privacy headers into all
|
||||||
/// outgoing responses.
|
/// outgoing responses.
|
||||||
|
@ -195,7 +195,7 @@ impl Fairing for Shield {
|
||||||
self.force_hsts.store(true, Ordering::Release);
|
self.force_hsts.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
info_span!("shield" [policies = self.policies.len()] => {
|
span_info!("shield", policies = self.policies.len() => {
|
||||||
self.policies.values().trace_all_info();
|
self.policies.values().trace_all_info();
|
||||||
|
|
||||||
if force_hsts {
|
if force_hsts {
|
||||||
|
@ -211,7 +211,7 @@ impl Fairing for Shield {
|
||||||
// the header is not already in the response.
|
// the header is not already in the response.
|
||||||
for header in self.policies.values() {
|
for header in self.policies.values() {
|
||||||
if response.headers().contains(header.name()) {
|
if response.headers().contains(header.name()) {
|
||||||
warn_span!("shield refusing to overwrite existing response header" => {
|
span_warn!("shield", "shield refusing to overwrite existing response header" => {
|
||||||
header.trace_warn();
|
header.trace_warn();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,56 +1,32 @@
|
||||||
macro_rules! declare_macro {
|
|
||||||
($($name:ident $level:ident),* $(,)?) => (
|
|
||||||
$(declare_macro!([$] $name $level);)*
|
|
||||||
);
|
|
||||||
|
|
||||||
([$d:tt] $name:ident $level:ident) => (
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! $name {
|
|
||||||
($d ($t:tt)*) => ($crate::tracing::$level!($d ($t)*));
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub use $name as $name;
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! declare_span_macro {
|
macro_rules! declare_span_macro {
|
||||||
($($name:ident $level:ident),* $(,)?) => (
|
($name:ident $level:ident) => (
|
||||||
$(declare_span_macro!([$] $name $level);)*
|
declare_span_macro!([$] $name $level);
|
||||||
);
|
);
|
||||||
|
|
||||||
([$d:tt] $name:ident $level:ident) => (
|
([$d:tt] $name:ident $level:ident) => (
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! $name {
|
macro_rules! $name {
|
||||||
($n:literal $d ([ $d ($f:tt)* ])? => $in_scope:expr) => ({
|
(@[$d ($t:tt)+] => $in_scope:expr) => ({
|
||||||
$crate::tracing::span!($crate::tracing::Level::$level, $n $d (, $d ($f)* )?)
|
$crate::tracing::span!($crate::tracing::Level::$level, $d ($t)+)
|
||||||
.in_scope(|| $in_scope);
|
.in_scope(|| $in_scope);
|
||||||
})
|
});
|
||||||
|
|
||||||
|
(@[$d ($t:tt)+] $token:tt $d ($rest:tt)*) => ({
|
||||||
|
$crate::trace::$name!(@[$d ($t)+ $token] $d ($rest)*);
|
||||||
|
});
|
||||||
|
|
||||||
|
// base case
|
||||||
|
($t:tt $d ($rest:tt)*) => ({
|
||||||
|
$crate::trace::$name!(@[$t] $d ($rest)*);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(hidden)]
|
||||||
pub use $name as $name;
|
pub use $name as $name;
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! span {
|
|
||||||
($level:expr, $($args:tt)*) => {{
|
|
||||||
match $level {
|
|
||||||
$crate::tracing::Level::ERROR =>
|
|
||||||
$crate::tracing::span!($crate::tracing::Level::ERROR, $($args)*),
|
|
||||||
$crate::tracing::Level::WARN =>
|
|
||||||
$crate::tracing::span!($crate::tracing::Level::WARN, $($args)*),
|
|
||||||
$crate::tracing::Level::INFO =>
|
|
||||||
$crate::tracing::span!($crate::tracing::Level::INFO, $($args)*),
|
|
||||||
$crate::tracing::Level::DEBUG =>
|
|
||||||
$crate::tracing::span!($crate::tracing::Level::DEBUG, $($args)*),
|
|
||||||
$crate::tracing::Level::TRACE =>
|
|
||||||
$crate::tracing::span!($crate::tracing::Level::TRACE, $($args)*),
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! event {
|
macro_rules! event {
|
||||||
|
@ -69,21 +45,64 @@ macro_rules! event {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-exports the macro at $path with the name $name. The point is to allow
|
||||||
|
// a `#[macro_use] extern crate rocket` to also automatically import the
|
||||||
|
// relevant tracing macros.
|
||||||
|
macro_rules! reexport {
|
||||||
|
($path:ident::$name:ident) => (
|
||||||
|
reexport!([$] $path::$name);
|
||||||
|
);
|
||||||
|
|
||||||
|
([ $d:tt ] $path:ident::$name:ident) => {
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! $name {
|
||||||
|
($d ($f:tt)*) => {
|
||||||
|
$crate::$path::$name!($d ($f)*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! span {
|
||||||
|
($level:expr, $($args:tt)*) => {{
|
||||||
|
match $level {
|
||||||
|
$crate::tracing::Level::ERROR =>
|
||||||
|
$crate::tracing::span!($crate::tracing::Level::ERROR, $($args)*),
|
||||||
|
$crate::tracing::Level::WARN =>
|
||||||
|
$crate::tracing::span!($crate::tracing::Level::WARN, $($args)*),
|
||||||
|
$crate::tracing::Level::INFO =>
|
||||||
|
$crate::tracing::span!($crate::tracing::Level::INFO, $($args)*),
|
||||||
|
$crate::tracing::Level::DEBUG =>
|
||||||
|
$crate::tracing::span!($crate::tracing::Level::DEBUG, $($args)*),
|
||||||
|
$crate::tracing::Level::TRACE =>
|
||||||
|
$crate::tracing::span!($crate::tracing::Level::TRACE, $($args)*),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use span as span;
|
||||||
|
|
||||||
|
declare_span_macro!(span_error ERROR);
|
||||||
|
declare_span_macro!(span_warn WARN);
|
||||||
|
declare_span_macro!(span_info INFO);
|
||||||
|
declare_span_macro!(span_debug DEBUG);
|
||||||
|
declare_span_macro!(span_trace TRACE);
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use event as event;
|
pub use event as event;
|
||||||
|
|
||||||
declare_macro!(
|
reexport!(tracing::error);
|
||||||
error error,
|
reexport!(tracing::warn);
|
||||||
info info,
|
reexport!(tracing::info);
|
||||||
trace trace,
|
reexport!(tracing::debug);
|
||||||
debug debug,
|
reexport!(tracing::trace);
|
||||||
warn warn
|
|
||||||
);
|
|
||||||
|
|
||||||
declare_span_macro!(
|
#[doc(hidden)] pub use tracing::error;
|
||||||
error_span ERROR,
|
#[doc(hidden)] pub use tracing::warn;
|
||||||
warn_span WARN,
|
#[doc(hidden)] pub use tracing::info;
|
||||||
info_span INFO,
|
#[doc(hidden)] pub use tracing::debug;
|
||||||
trace_span TRACE,
|
#[doc(hidden)] pub use tracing::trace;
|
||||||
debug_span DEBUG,
|
|
||||||
);
|
|
||||||
|
|
|
@ -8,14 +8,18 @@ pub mod subscriber;
|
||||||
|
|
||||||
pub(crate) mod level;
|
pub(crate) mod level;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use macros::*;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use traceable::{Trace, TraceAll};
|
pub use traceable::{Trace, TraceAll};
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use macros::*;
|
pub use tracing::{Level, level_filters::LevelFilter};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum TraceFormat {
|
pub enum TraceFormat {
|
||||||
#[serde(rename = "pretty")]
|
#[serde(rename = "pretty")]
|
||||||
#[serde(alias = "PRETTY")]
|
#[serde(alias = "PRETTY")]
|
||||||
|
@ -27,6 +31,9 @@ pub enum TraceFormat {
|
||||||
|
|
||||||
#[cfg_attr(nightly, doc(cfg(feature = "trace")))]
|
#[cfg_attr(nightly, doc(cfg(feature = "trace")))]
|
||||||
pub fn init<'a, T: Into<Option<&'a crate::Config>>>(config: T) {
|
pub fn init<'a, T: Into<Option<&'a crate::Config>>>(config: T) {
|
||||||
|
#[cfg(not(feature = "trace"))]
|
||||||
|
let _ = config;
|
||||||
|
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
crate::trace::subscriber::RocketDynFmt::init(config.into())
|
crate::trace::subscriber::RocketDynFmt::init(config.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ impl RocketFmt<Pretty> {
|
||||||
MARKER.get(self.state().depth as usize).copied().unwrap_or("-- ")
|
MARKER.get(self.state().depth as usize).copied().unwrap_or("-- ")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emoji(&self, emoji: &'static str) -> Painted<&'static str> {
|
fn emoji(&self, _emoji: &'static str) -> Painted<&'static str> {
|
||||||
#[cfg(windows)] { "".paint(self.style).mask() }
|
#[cfg(windows)] { "".paint(self.style).mask() }
|
||||||
#[cfg(not(windows))] { emoji.paint(self.style).mask() }
|
#[cfg(not(windows))] { _emoji.paint(self.style).mask() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prefix<'a>(&self, meta: &'a Metadata<'_>) -> impl fmt::Display + 'a {
|
fn prefix<'a>(&self, meta: &'a Metadata<'_>) -> impl fmt::Display + 'a {
|
||||||
|
@ -79,7 +79,7 @@ impl<S: Subscriber + for<'a> LookupSpan<'a>> Layer<S> for RocketFmt<Pretty> {
|
||||||
"liftoff" => {
|
"liftoff" => {
|
||||||
let prefix = self.prefix(meta);
|
let prefix = self.prefix(meta);
|
||||||
println!("{prefix}{}{} {}", self.emoji("🚀 "),
|
println!("{prefix}{}{} {}", self.emoji("🚀 "),
|
||||||
"Rocket has launched from".paint(style).primary().bold(),
|
"Rocket has launched on".paint(style).primary().bold(),
|
||||||
&data["endpoint"].paint(style).primary().bold().underline());
|
&data["endpoint"].paint(style).primary().bold().underline());
|
||||||
},
|
},
|
||||||
"route" => println!("{}", Formatter(|f| {
|
"route" => println!("{}", Formatter(|f| {
|
||||||
|
@ -98,7 +98,13 @@ impl<S: Subscriber + for<'a> LookupSpan<'a>> Layer<S> for RocketFmt<Pretty> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(name) = data.get("name") {
|
if let Some(name) = data.get("name") {
|
||||||
write!(f, " ({})", name.paint(style.bold().bright()))?;
|
write!(f, " ({}", name.paint(style.bold().bright()))?;
|
||||||
|
|
||||||
|
if let Some(location) = data.get("location") {
|
||||||
|
write!(f, " {}", location.paint(style.dim()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, ")")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -113,7 +119,13 @@ impl<S: Subscriber + for<'a> LookupSpan<'a>> Layer<S> for RocketFmt<Pretty> {
|
||||||
|
|
||||||
write!(f, "{}", &data["uri.base"].paint(style.primary()))?;
|
write!(f, "{}", &data["uri.base"].paint(style.primary()))?;
|
||||||
if let Some(name) = data.get("name") {
|
if let Some(name) = data.get("name") {
|
||||||
write!(f, " ({})", name.paint(style.bold().bright()))?;
|
write!(f, " ({}", name.paint(style.bold().bright()))?;
|
||||||
|
|
||||||
|
if let Some(location) = data.get("location") {
|
||||||
|
write!(f, " {}", location.paint(style.dim()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, ")")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -47,10 +47,15 @@ impl Trace for Figment {
|
||||||
fn trace(&self, level: Level) {
|
fn trace(&self, level: Level) {
|
||||||
for param in Config::PARAMETERS {
|
for param in Config::PARAMETERS {
|
||||||
if let Some(source) = self.find_metadata(param) {
|
if let Some(source) = self.find_metadata(param) {
|
||||||
|
if param.contains("secret") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
event! { level, "figment",
|
event! { level, "figment",
|
||||||
param,
|
param,
|
||||||
%source.name,
|
%source.name,
|
||||||
source.source = source.source.as_ref().map(display),
|
source.source = source.source.as_ref().map(display),
|
||||||
|
value = self.find_value(param).ok().map(debug),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,6 +147,9 @@ impl Trace for Route {
|
||||||
uri.base = %self.uri.base(),
|
uri.base = %self.uri.base(),
|
||||||
uri.unmounted = %self.uri.unmounted(),
|
uri.unmounted = %self.uri.unmounted(),
|
||||||
format = self.format.as_ref().map(display),
|
format = self.format.as_ref().map(display),
|
||||||
|
location = self.location.as_ref()
|
||||||
|
.map(|(file, line, _)| Formatter(move |f| write!(f, "{file}:{line}")))
|
||||||
|
.map(display),
|
||||||
}
|
}
|
||||||
|
|
||||||
event! { Level::DEBUG, "sentinels",
|
event! { Level::DEBUG, "sentinels",
|
||||||
|
@ -165,6 +173,9 @@ impl Trace for Catcher {
|
||||||
}),
|
}),
|
||||||
rank = self.rank,
|
rank = self.rank,
|
||||||
uri.base = %self.base(),
|
uri.base = %self.base(),
|
||||||
|
location = self.location.as_ref()
|
||||||
|
.map(|(file, line, _)| Formatter(move |f| write!(f, "{file}:{line}")))
|
||||||
|
.map(display),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,17 +216,14 @@ impl Trace for figment::error::Kind {
|
||||||
impl Trace for figment::Error {
|
impl Trace for figment::Error {
|
||||||
fn trace(&self, _: Level) {
|
fn trace(&self, _: Level) {
|
||||||
for e in self.clone() {
|
for e in self.clone() {
|
||||||
let span = tracing::error_span! {
|
span_error!("config",
|
||||||
"config",
|
|
||||||
key = (!e.path.is_empty()).then_some(&e.path).and_then(|path| {
|
key = (!e.path.is_empty()).then_some(&e.path).and_then(|path| {
|
||||||
let (profile, metadata) = (e.profile.as_ref()?, e.metadata.as_ref()?);
|
let (profile, metadata) = (e.profile.as_ref()?, e.metadata.as_ref()?);
|
||||||
Some(metadata.interpolate(profile, path))
|
Some(metadata.interpolate(profile, path))
|
||||||
}),
|
}),
|
||||||
source.name = e.metadata.as_ref().map(|m| &*m.name),
|
source.name = e.metadata.as_ref().map(|m| &*m.name),
|
||||||
source.source = e.metadata.as_ref().and_then(|m| m.source.as_ref()).map(display),
|
source.source = e.metadata.as_ref().and_then(|m| m.source.as_ref()).map(display)
|
||||||
};
|
=> e.kind.trace_error());
|
||||||
|
|
||||||
span.in_scope(|| e.kind.trace_error());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,7 +306,7 @@ impl Trace for ErrorKind {
|
||||||
e.trace(level);
|
e.trace(level);
|
||||||
} else {
|
} else {
|
||||||
event!(level, "error::bind",
|
event!(level, "error::bind",
|
||||||
?error,
|
reason = %error,
|
||||||
endpoint = endpoint.as_ref().map(display),
|
endpoint = endpoint.as_ref().map(display),
|
||||||
"binding to network interface failed"
|
"binding to network interface failed"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use rocket::{*, error::ErrorKind::SentinelAborts};
|
use rocket::{*, either::Either, error::ErrorKind::SentinelAborts};
|
||||||
|
|
||||||
#[get("/two")]
|
#[get("/two")]
|
||||||
fn two_states(_one: &State<u32>, _two: &State<String>) {}
|
fn two_states(_one: &State<u32>, _two: &State<String>) {}
|
||||||
|
|
|
@ -19,6 +19,7 @@ port = 8000
|
||||||
workers = 1
|
workers = 1
|
||||||
keep_alive = 0
|
keep_alive = 0
|
||||||
log_level = "info"
|
log_level = "info"
|
||||||
|
log_format = "pretty"
|
||||||
|
|
||||||
[release]
|
[release]
|
||||||
address = "127.0.0.1"
|
address = "127.0.0.1"
|
||||||
|
@ -26,6 +27,7 @@ port = 8000
|
||||||
workers = 12
|
workers = 12
|
||||||
keep_alive = 5
|
keep_alive = 5
|
||||||
log_level = "error"
|
log_level = "error"
|
||||||
|
log_format = "compact"
|
||||||
# NOTE: Don't (!) use this key! Generate your own and keep it private!
|
# NOTE: Don't (!) use this key! Generate your own and keep it private!
|
||||||
# e.g. via `head -c64 /dev/urandom | base64`
|
# e.g. via `head -c64 /dev/urandom | base64`
|
||||||
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
|
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use rocket::config::{Config, /* LogLevel */};
|
use rocket::config::Config;
|
||||||
|
use rocket::trace::{Level, TraceFormat};
|
||||||
|
|
||||||
async fn test_config(profile: &str) {
|
async fn test_config(profile: &str) {
|
||||||
let provider = Config::figment().select(profile);
|
let provider = Config::figment().select(profile);
|
||||||
|
@ -8,12 +9,14 @@ async fn test_config(profile: &str) {
|
||||||
"debug" => {
|
"debug" => {
|
||||||
assert_eq!(config.workers, 1);
|
assert_eq!(config.workers, 1);
|
||||||
assert_eq!(config.keep_alive, 0);
|
assert_eq!(config.keep_alive, 0);
|
||||||
// assert_eq!(config.log_level, LogLevel::Normal);
|
assert_eq!(config.log_level, Some(Level::INFO));
|
||||||
|
assert_eq!(config.log_format, TraceFormat::Pretty);
|
||||||
}
|
}
|
||||||
"release" => {
|
"release" => {
|
||||||
assert_eq!(config.workers, 12);
|
assert_eq!(config.workers, 12);
|
||||||
assert_eq!(config.keep_alive, 5);
|
assert_eq!(config.keep_alive, 5);
|
||||||
// assert_eq!(config.log_level, LogLevel::Critical);
|
assert_eq!(config.log_level, Some(Level::ERROR));
|
||||||
|
assert_eq!(config.log_format, TraceFormat::Compact);
|
||||||
assert!(!config.secret_key.is_zero());
|
assert!(!config.secret_key.is_zero());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -76,7 +76,7 @@ fn rocket() -> _ {
|
||||||
.attach(AdHoc::on_request("PUT Rewriter", |req, _| {
|
.attach(AdHoc::on_request("PUT Rewriter", |req, _| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
if req.uri().path() == "/" {
|
if req.uri().path() == "/" {
|
||||||
info_span!("PUT rewriter" => {
|
span_info!("PUT rewriter" => {
|
||||||
req.trace_info();
|
req.trace_info();
|
||||||
info!("changing method to `PUT`");
|
info!("changing method to `PUT`");
|
||||||
req.set_method(Method::Put);
|
req.set_method(Method::Put);
|
||||||
|
|
|
@ -6,4 +6,4 @@ edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { path = "../../core/lib", features = ["secrets"] }
|
rocket = { path = "../../core/lib" }
|
||||||
|
|
|
@ -38,9 +38,6 @@ fn wave(name: &str, age: u8) -> String {
|
||||||
format!("👋 Hello, {} year old named {}!", age, name)
|
format!("👋 Hello, {} year old named {}!", age, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<a>/<b>")]
|
|
||||||
fn f(a: usize, b: usize) { }
|
|
||||||
|
|
||||||
// Note: without the `..` in `opt..`, we'd need to pass `opt.emoji`, `opt.name`.
|
// Note: without the `..` in `opt..`, we'd need to pass `opt.emoji`, `opt.name`.
|
||||||
//
|
//
|
||||||
// Try visiting:
|
// Try visiting:
|
||||||
|
@ -54,7 +51,7 @@ fn f(a: usize, b: usize) { }
|
||||||
// http://127.0.0.1:8000/?name=Rocketeer&lang=en&emoji
|
// http://127.0.0.1:8000/?name=Rocketeer&lang=en&emoji
|
||||||
// http://127.0.0.1:8000/?lang=ru&emoji&name=Rocketeer
|
// http://127.0.0.1:8000/?lang=ru&emoji&name=Rocketeer
|
||||||
#[get("/?<lang>&<opt..>")]
|
#[get("/?<lang>&<opt..>")]
|
||||||
async fn hello(lang: Option<Lang>, opt: Options<'_>) -> String {
|
fn hello(lang: Option<Lang>, opt: Options<'_>) -> String {
|
||||||
let mut greeting = String::new();
|
let mut greeting = String::new();
|
||||||
if opt.emoji {
|
if opt.emoji {
|
||||||
greeting.push_str("👋 ");
|
greeting.push_str("👋 ");
|
||||||
|
|
|
@ -159,7 +159,7 @@ fn not_found(request: &Request<'_>) -> content::RawHtml<String> {
|
||||||
|
|
||||||
/******************************* `Either` Responder ***************************/
|
/******************************* `Either` Responder ***************************/
|
||||||
|
|
||||||
use rocket::Either;
|
use rocket::either::Either;
|
||||||
use rocket::response::content::{RawJson, RawMsgPack};
|
use rocket::response::content::{RawJson, RawMsgPack};
|
||||||
use rocket::http::uncased::AsUncased;
|
use rocket::http::uncased::AsUncased;
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl Redirector {
|
||||||
pub async fn try_launch(self, config: Config) -> Result<Rocket<Ignite>, Error> {
|
pub async fn try_launch(self, config: Config) -> Result<Rocket<Ignite>, Error> {
|
||||||
use rocket::http::Method::*;
|
use rocket::http::Method::*;
|
||||||
|
|
||||||
rocket::info_span!("HTTP -> HTTPS Redirector" => {
|
rocket::span_info!("HTTP -> HTTPS Redirector" => {
|
||||||
info!(from = self.0, to = config.tls_addr.port(), "redirecting");
|
info!(from = self.0, to = config.tls_addr.port(), "redirecting");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ impl Fairing for Redirector {
|
||||||
|
|
||||||
async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
|
async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
|
||||||
let Some(tls_addr) = rocket.endpoints().find_map(|e| e.tls()?.tcp()) else {
|
let Some(tls_addr) = rocket.endpoints().find_map(|e| e.tls()?.tcp()) else {
|
||||||
rocket::warn_span!("HTTP -> HTTPS Redirector" => {
|
rocket::span_warn!("HTTP -> HTTPS Redirector" => {
|
||||||
warn!("Main instance is not being served over TLS/TCP.\n\
|
warn!("Main instance is not being served over TLS/TCP.\n\
|
||||||
Redirector refusing to start.");
|
Redirector refusing to start.");
|
||||||
});
|
});
|
||||||
|
@ -95,7 +95,7 @@ impl Fairing for Redirector {
|
||||||
let shutdown = rocket.shutdown();
|
let shutdown = rocket.shutdown();
|
||||||
rocket::tokio::spawn(async move {
|
rocket::tokio::spawn(async move {
|
||||||
if let Err(e) = this.try_launch(config).await {
|
if let Err(e) = this.try_launch(config).await {
|
||||||
error_span!("failed to start HTTP -> HTTPS redirector" => {
|
span_error!("HTTP -> HTTPS Redirector", "failed to start" => {
|
||||||
e.trace_error();
|
e.trace_error();
|
||||||
info!("shutting down main instance");
|
info!("shutting down main instance");
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,6 +13,7 @@ procspawn = "1"
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
ipc-channel = "0.18"
|
ipc-channel = "0.18"
|
||||||
rustls-pemfile = "2.1"
|
rustls-pemfile = "2.1"
|
||||||
|
inventory = "0.3.15"
|
||||||
|
|
||||||
[dependencies.nix]
|
[dependencies.nix]
|
||||||
version = "0.28"
|
version = "0.28"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::time::Duration;
|
use std::{str::FromStr, time::Duration};
|
||||||
|
|
||||||
use reqwest::blocking::{ClientBuilder, RequestBuilder};
|
use reqwest::blocking::{ClientBuilder, RequestBuilder};
|
||||||
use rocket::http::{ext::IntoOwned, uri::{Absolute, Uri}};
|
use rocket::http::{ext::IntoOwned, uri::{Absolute, Uri}, Method};
|
||||||
|
|
||||||
use crate::{Result, Error, Server};
|
use crate::{Result, Error, Server};
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ impl Client {
|
||||||
.connect_timeout(Duration::from_secs(5))
|
.connect_timeout(Duration::from_secs(5))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, server: &Server, url: &str) -> Result<RequestBuilder> {
|
pub fn request(&self, server: &Server, method: Method, url: &str) -> Result<RequestBuilder> {
|
||||||
let uri = match Uri::parse_any(url).map_err(|e| e.into_owned())? {
|
let uri = match Uri::parse_any(url).map_err(|e| e.into_owned())? {
|
||||||
Uri::Origin(uri) => {
|
Uri::Origin(uri) => {
|
||||||
let proto = if server.tls { "https" } else { "http" };
|
let proto = if server.tls { "https" } else { "http" };
|
||||||
|
@ -45,7 +45,16 @@ impl Client {
|
||||||
uri => return Err(Error::InvalidUri(uri.into_owned())),
|
uri => return Err(Error::InvalidUri(uri.into_owned())),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(self.client.get(uri.to_string()))
|
let method = reqwest::Method::from_str(method.as_str()).unwrap();
|
||||||
|
Ok(self.client.request(method, uri.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, server: &Server, url: &str) -> Result<RequestBuilder> {
|
||||||
|
self.request(server, Method::Get, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post(&self, server: &Server, url: &str) -> Result<RequestBuilder> {
|
||||||
|
self.request(server, Method::Post, url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
use rocket::{Build, Rocket};
|
||||||
|
|
||||||
|
use testbench::{Result, Error};
|
||||||
|
|
||||||
|
pub static DEFAULT_CONFIG: &str = r#"
|
||||||
|
[default]
|
||||||
|
address = "tcp:127.0.0.1"
|
||||||
|
workers = 2
|
||||||
|
port = 0
|
||||||
|
cli_colors = false
|
||||||
|
log_level = "debug"
|
||||||
|
secret_key = "itlYmFR2vYKrOmFhupMIn/hyB6lYCCTXz4yaQX89XVg="
|
||||||
|
|
||||||
|
[default.shutdown]
|
||||||
|
grace = 1
|
||||||
|
mercy = 1
|
||||||
|
"#;
|
||||||
|
|
||||||
|
pub static TLS_CONFIG: &str = r#"
|
||||||
|
[default.tls]
|
||||||
|
certs = "{ROCKET}/examples/tls/private/rsa_sha256_cert.pem"
|
||||||
|
key = "{ROCKET}/examples/tls/private/rsa_sha256_key.pem"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
pub trait RocketExt {
|
||||||
|
fn default() -> Self;
|
||||||
|
fn tls_default() -> Self;
|
||||||
|
fn reconfigure_with_toml(self, toml: &str) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RocketExt for Rocket<Build> {
|
||||||
|
fn default() -> Self {
|
||||||
|
rocket::build().reconfigure_with_toml(DEFAULT_CONFIG)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tls_default() -> Self {
|
||||||
|
rocket::build()
|
||||||
|
.reconfigure_with_toml(DEFAULT_CONFIG)
|
||||||
|
.reconfigure_with_toml(TLS_CONFIG)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconfigure_with_toml(self, toml: &str) -> Self {
|
||||||
|
use rocket::figment::{Figment, providers::{Format, Toml}};
|
||||||
|
|
||||||
|
let toml = toml.replace("{ROCKET}", rocket::fs::relative!("../"));
|
||||||
|
let config = Figment::from(self.figment())
|
||||||
|
.merge(Toml::string(&toml).nested());
|
||||||
|
|
||||||
|
self.reconfigure(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(path: &str) -> Result<Vec<u8>> {
|
||||||
|
let path = path.replace("{ROCKET}", rocket::fs::relative!("../"));
|
||||||
|
Ok(std::fs::read(path)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cert(path: &str) -> Result<Vec<u8>> {
|
||||||
|
let mut data = std::io::Cursor::new(read(path)?);
|
||||||
|
let cert = rustls_pemfile::certs(&mut data).last();
|
||||||
|
Ok(cert.ok_or(Error::MissingCertificate)??.to_vec())
|
||||||
|
}
|
|
@ -1,480 +1,19 @@
|
||||||
use std::net::{Ipv4Addr, SocketAddr};
|
mod runner;
|
||||||
use std::process::ExitCode;
|
mod servers;
|
||||||
use std::time::Duration;
|
mod config;
|
||||||
|
|
||||||
use rocket::tokio::net::TcpListener;
|
pub mod prelude {
|
||||||
use rocket::yansi::Paint;
|
pub use rocket::*;
|
||||||
use rocket::{get, routes, Build, Rocket, State};
|
pub use rocket::fairing::*;
|
||||||
use rocket::listener::{unix::UnixListener, Endpoint};
|
pub use rocket::response::stream::*;
|
||||||
use rocket::tls::TlsListener;
|
|
||||||
|
|
||||||
use reqwest::{tls::TlsInfo, Identity};
|
pub use testbench::{Error, Result, *};
|
||||||
|
pub use crate::register;
|
||||||
use testbench::*;
|
pub use crate::config::*;
|
||||||
|
|
||||||
static DEFAULT_CONFIG: &str = r#"
|
|
||||||
[default]
|
|
||||||
address = "tcp:127.0.0.1"
|
|
||||||
workers = 2
|
|
||||||
port = 0
|
|
||||||
cli_colors = false
|
|
||||||
secret_key = "itlYmFR2vYKrOmFhupMIn/hyB6lYCCTXz4yaQX89XVg="
|
|
||||||
|
|
||||||
[default.shutdown]
|
|
||||||
grace = 1
|
|
||||||
mercy = 1
|
|
||||||
"#;
|
|
||||||
|
|
||||||
static TLS_CONFIG: &str = r#"
|
|
||||||
[default.tls]
|
|
||||||
certs = "{ROCKET}/examples/tls/private/rsa_sha256_cert.pem"
|
|
||||||
key = "{ROCKET}/examples/tls/private/rsa_sha256_key.pem"
|
|
||||||
"#;
|
|
||||||
|
|
||||||
trait RocketExt {
|
|
||||||
fn default() -> Self;
|
|
||||||
fn tls_default() -> Self;
|
|
||||||
fn reconfigure_with_toml(self, toml: &str) -> Self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RocketExt for Rocket<Build> {
|
pub use runner::Test;
|
||||||
fn default() -> Self {
|
|
||||||
rocket::build().reconfigure_with_toml(DEFAULT_CONFIG)
|
fn main() -> std::process::ExitCode {
|
||||||
|
runner::run()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tls_default() -> Self {
|
|
||||||
rocket::build()
|
|
||||||
.reconfigure_with_toml(DEFAULT_CONFIG)
|
|
||||||
.reconfigure_with_toml(TLS_CONFIG)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reconfigure_with_toml(self, toml: &str) -> Self {
|
|
||||||
use rocket::figment::{Figment, providers::{Format, Toml}};
|
|
||||||
|
|
||||||
let toml = toml.replace("{ROCKET}", rocket::fs::relative!("../"));
|
|
||||||
let config = Figment::from(self.figment())
|
|
||||||
.merge(Toml::string(&toml).nested());
|
|
||||||
|
|
||||||
self.reconfigure(config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(path: &str) -> Result<Vec<u8>> {
|
|
||||||
let path = path.replace("{ROCKET}", rocket::fs::relative!("../"));
|
|
||||||
Ok(std::fs::read(path)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cert(path: &str) -> Result<Vec<u8>> {
|
|
||||||
let mut data = std::io::Cursor::new(read(path)?);
|
|
||||||
let cert = rustls_pemfile::certs(&mut data).last();
|
|
||||||
Ok(cert.ok_or(Error::MissingCertificate)??.to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_fail() -> Result<()> {
|
|
||||||
use rocket::fairing::AdHoc;
|
|
||||||
|
|
||||||
let server = spawn! {
|
|
||||||
let fail = AdHoc::try_on_ignite("FailNow", |rocket| async { Err(rocket) });
|
|
||||||
Rocket::default().attach(fail)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(Error::Liftoff(stdout, _)) = server {
|
|
||||||
// assert!(stdout.contains("Rocket failed to launch due to failing fairings"));
|
|
||||||
// assert!(stdout.contains("FailNow"));
|
|
||||||
} else {
|
|
||||||
panic!("unexpected result: {server:#?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infinite() -> Result<()> {
|
|
||||||
use rocket::response::stream::TextStream;
|
|
||||||
|
|
||||||
let mut server = spawn! {
|
|
||||||
#[get("/")]
|
|
||||||
fn infinite() -> TextStream![&'static str] {
|
|
||||||
TextStream! {
|
|
||||||
loop {
|
|
||||||
yield rocket::futures::future::pending::<&str>().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rocket::default().mount("/", routes![infinite])
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let client = Client::default();
|
|
||||||
client.get(&server, "/")?.send()?;
|
|
||||||
server.terminate()?;
|
|
||||||
|
|
||||||
let stdout = server.read_stdout()?;
|
|
||||||
// assert!(stdout.contains("Rocket has launched on http"));
|
|
||||||
// assert!(stdout.contains("GET /"));
|
|
||||||
// assert!(stdout.contains("Graceful shutdown completed"));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tls_info() -> Result<()> {
|
|
||||||
#[get("/")]
|
|
||||||
fn hello_world(endpoint: &Endpoint) -> String {
|
|
||||||
format!("Hello, {endpoint}!")
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut server = spawn! {
|
|
||||||
Rocket::tls_default().mount("/", routes![hello_world])
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let client = Client::default();
|
|
||||||
let response = client.get(&server, "/")?.send()?;
|
|
||||||
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
|
||||||
assert!(!tls.peer_certificate().unwrap().is_empty());
|
|
||||||
assert!(response.text()?.starts_with("Hello, https://127.0.0.1"));
|
|
||||||
|
|
||||||
server.terminate()?;
|
|
||||||
let stdout = server.read_stdout()?;
|
|
||||||
// assert!(stdout.contains("Rocket has launched on https"));
|
|
||||||
// assert!(stdout.contains("Graceful shutdown completed"));
|
|
||||||
// assert!(stdout.contains("GET /"));
|
|
||||||
|
|
||||||
let server = Server::spawn((), |(token, _)| {
|
|
||||||
let rocket = rocket::build()
|
|
||||||
.reconfigure_with_toml(TLS_CONFIG)
|
|
||||||
.mount("/", routes![hello_world]);
|
|
||||||
|
|
||||||
token.with_launch(rocket, |rocket| {
|
|
||||||
let config = rocket.figment().extract_inner("tls");
|
|
||||||
rocket.try_launch_on(async move {
|
|
||||||
let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 0);
|
|
||||||
let listener = TcpListener::bind(addr).await?;
|
|
||||||
TlsListener::from(listener, config?).await
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}).unwrap();
|
|
||||||
|
|
||||||
let client = Client::default();
|
|
||||||
let response = client.get(&server, "/")?.send()?;
|
|
||||||
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
|
||||||
assert!(!tls.peer_certificate().unwrap().is_empty());
|
|
||||||
assert!(response.text()?.starts_with("Hello, https://127.0.0.1"));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tls_resolver() -> Result<()> {
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use rocket::tls::{Resolver, TlsConfig, ClientHello, ServerConfig};
|
|
||||||
|
|
||||||
struct CountingResolver {
|
|
||||||
config: Arc<ServerConfig>,
|
|
||||||
counter: Arc<AtomicUsize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl Resolver for CountingResolver {
|
|
||||||
async fn init(rocket: &Rocket<Build>) -> rocket::tls::Result<Self> {
|
|
||||||
let config: TlsConfig = rocket.figment().extract_inner("tls")?;
|
|
||||||
let config = Arc::new(config.server_config().await?);
|
|
||||||
let counter = rocket.state::<Arc<AtomicUsize>>().unwrap().clone();
|
|
||||||
Ok(Self { config, counter })
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn resolve(&self, _: ClientHello<'_>) -> Option<Arc<ServerConfig>> {
|
|
||||||
self.counter.fetch_add(1, Ordering::Release);
|
|
||||||
Some(self.config.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let server = spawn! {
|
|
||||||
#[get("/count")]
|
|
||||||
fn count(counter: &State<Arc<AtomicUsize>>) -> String {
|
|
||||||
counter.load(Ordering::Acquire).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
let counter = Arc::new(AtomicUsize::new(0));
|
|
||||||
Rocket::tls_default()
|
|
||||||
.manage(counter)
|
|
||||||
.mount("/", routes![count])
|
|
||||||
.attach(CountingResolver::fairing())
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let client = Client::default();
|
|
||||||
let response = client.get(&server, "/count")?.send()?;
|
|
||||||
assert_eq!(response.text()?, "1");
|
|
||||||
|
|
||||||
// Use a new client so we get a new TLS session.
|
|
||||||
let client = Client::default();
|
|
||||||
let response = client.get(&server, "/count")?.send()?;
|
|
||||||
assert_eq!(response.text()?, "2");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_mtls(mandatory: bool) -> Result<()> {
|
|
||||||
let server = spawn!(mandatory: bool => {
|
|
||||||
let mtls_config = format!(r#"
|
|
||||||
[default.tls.mutual]
|
|
||||||
ca_certs = "{{ROCKET}}/examples/tls/private/ca_cert.pem"
|
|
||||||
mandatory = {mandatory}
|
|
||||||
"#);
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
fn hello(cert: rocket::mtls::Certificate<'_>) -> String {
|
|
||||||
format!("{}:{}[{}] {}", cert.serial(), cert.version(), cert.issuer(), cert.subject())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/", rank = 2)]
|
|
||||||
fn hi() -> &'static str {
|
|
||||||
"Hello!"
|
|
||||||
}
|
|
||||||
|
|
||||||
Rocket::tls_default()
|
|
||||||
.reconfigure_with_toml(&mtls_config)
|
|
||||||
.mount("/", routes![hello, hi])
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let pem = read("{ROCKET}/examples/tls/private/client.pem")?;
|
|
||||||
let client: Client = Client::build()
|
|
||||||
.identity(Identity::from_pem(&pem)?)
|
|
||||||
.try_into()?;
|
|
||||||
|
|
||||||
let response = client.get(&server, "/")?.send()?;
|
|
||||||
assert_eq!(response.text()?,
|
|
||||||
"611895682361338926795452113263857440769284805738:2\
|
|
||||||
[C=US, ST=CA, O=Rocket CA, CN=Rocket Root CA] \
|
|
||||||
C=US, ST=California, L=Silicon Valley, O=Rocket, \
|
|
||||||
CN=Rocket TLS Example, Email=example@rocket.local");
|
|
||||||
|
|
||||||
let client = Client::default();
|
|
||||||
let response = client.get(&server, "/")?.send();
|
|
||||||
if mandatory {
|
|
||||||
assert!(response.unwrap_err().is_request());
|
|
||||||
} else {
|
|
||||||
assert_eq!(response?.text()?, "Hello!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tls_mtls() -> Result<()> {
|
|
||||||
test_mtls(false)?;
|
|
||||||
test_mtls(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sni_resolver() -> Result<()> {
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use rocket::http::uri::Host;
|
|
||||||
use rocket::tls::{Resolver, TlsConfig, ClientHello, ServerConfig};
|
|
||||||
|
|
||||||
struct SniResolver {
|
|
||||||
default: Arc<ServerConfig>,
|
|
||||||
map: HashMap<Host<'static>, Arc<ServerConfig>>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl Resolver for SniResolver {
|
|
||||||
async fn init(rocket: &Rocket<Build>) -> rocket::tls::Result<Self> {
|
|
||||||
let default: TlsConfig = rocket.figment().extract_inner("tls")?;
|
|
||||||
let sni: HashMap<Host<'_>, TlsConfig> = rocket.figment().extract_inner("tls.sni")?;
|
|
||||||
|
|
||||||
let default = Arc::new(default.server_config().await?);
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
for (host, config) in sni {
|
|
||||||
let config = config.server_config().await?;
|
|
||||||
map.insert(host, Arc::new(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(SniResolver { default, map })
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn resolve(&self, hello: ClientHello<'_>) -> Option<Arc<ServerConfig>> {
|
|
||||||
if let Some(Ok(host)) = hello.server_name().map(Host::parse) {
|
|
||||||
if let Some(config) = self.map.get(&host) {
|
|
||||||
return Some(config.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(self.default.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static SNI_TLS_CONFIG: &str = r#"
|
|
||||||
[default.tls]
|
|
||||||
certs = "{ROCKET}/examples/tls/private/rsa_sha256_cert.pem"
|
|
||||||
key = "{ROCKET}/examples/tls/private/rsa_sha256_key.pem"
|
|
||||||
|
|
||||||
[default.tls.sni."sni1.dev"]
|
|
||||||
certs = "{ROCKET}/examples/tls/private/ecdsa_nistp256_sha256_cert.pem"
|
|
||||||
key = "{ROCKET}/examples/tls/private/ecdsa_nistp256_sha256_key_pkcs8.pem"
|
|
||||||
|
|
||||||
[default.tls.sni."sni2.dev"]
|
|
||||||
certs = "{ROCKET}/examples/tls/private/ed25519_cert.pem"
|
|
||||||
key = "{ROCKET}/examples/tls/private/ed25519_key.pem"
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let server = spawn! {
|
|
||||||
#[get("/")] fn index() { }
|
|
||||||
|
|
||||||
Rocket::default()
|
|
||||||
.reconfigure_with_toml(SNI_TLS_CONFIG)
|
|
||||||
.mount("/", routes![index])
|
|
||||||
.attach(SniResolver::fairing())
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let client: Client = Client::build()
|
|
||||||
.resolve("unknown.dev", server.socket_addr())
|
|
||||||
.resolve("sni1.dev", server.socket_addr())
|
|
||||||
.resolve("sni2.dev", server.socket_addr())
|
|
||||||
.try_into()?;
|
|
||||||
|
|
||||||
let response = client.get(&server, "https://unknown.dev")?.send()?;
|
|
||||||
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
|
||||||
let expected = cert("{ROCKET}/examples/tls/private/rsa_sha256_cert.pem")?;
|
|
||||||
assert_eq!(tls.peer_certificate().unwrap(), expected);
|
|
||||||
|
|
||||||
let response = client.get(&server, "https://sni1.dev")?.send()?;
|
|
||||||
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
|
||||||
let expected = cert("{ROCKET}/examples/tls/private/ecdsa_nistp256_sha256_cert.pem")?;
|
|
||||||
assert_eq!(tls.peer_certificate().unwrap(), expected);
|
|
||||||
|
|
||||||
let response = client.get(&server, "https://sni2.dev")?.send()?;
|
|
||||||
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
|
||||||
let expected = cert("{ROCKET}/examples/tls/private/ed25519_cert.pem")?;
|
|
||||||
assert_eq!(tls.peer_certificate().unwrap(), expected);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tcp_unix_listener_fail() -> Result<()> {
|
|
||||||
let server = spawn! {
|
|
||||||
Rocket::default().reconfigure_with_toml("[default]\naddress = 123")
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(Error::Liftoff(stdout, _)) = server {
|
|
||||||
// assert!(stdout.contains("expected valid TCP (ip) or unix (path)"));
|
|
||||||
// assert!(stdout.contains("default.address"));
|
|
||||||
} else {
|
|
||||||
panic!("unexpected result: {server:#?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let server = Server::spawn((), |(token, _)| {
|
|
||||||
let rocket = Rocket::default().reconfigure_with_toml("[default]\naddress = \"unix:foo\"");
|
|
||||||
token.launch_with::<TcpListener>(rocket)
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Err(Error::Liftoff(stdout, _)) = server {
|
|
||||||
// assert!(stdout.contains("invalid tcp endpoint: unix:foo"));
|
|
||||||
} else {
|
|
||||||
panic!("unexpected result: {server:#?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let server = Server::spawn((), |(token, _)| {
|
|
||||||
token.launch_with::<UnixListener>(Rocket::default())
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Err(Error::Liftoff(stdout, _)) = server {
|
|
||||||
// assert!(stdout.contains("invalid unix endpoint: tcp:127.0.0.1:8000"));
|
|
||||||
} else {
|
|
||||||
panic!("unexpected result: {server:#?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! tests {
|
|
||||||
($($f:ident),* $(,)?) => {[
|
|
||||||
$(Test {
|
|
||||||
name: stringify!($f),
|
|
||||||
run: |_: ()| $f().map_err(|e| e.to_string()),
|
|
||||||
}),*
|
|
||||||
]};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Test {
|
|
||||||
name: &'static str,
|
|
||||||
run: fn(()) -> Result<(), String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static TESTS: &[Test] = &tests![
|
|
||||||
run_fail, infinite, tls_info, tls_resolver, tls_mtls, sni_resolver,
|
|
||||||
tcp_unix_listener_fail
|
|
||||||
];
|
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
|
||||||
procspawn::init();
|
|
||||||
|
|
||||||
let filter = std::env::args().nth(1).unwrap_or_default();
|
|
||||||
let filtered = TESTS.into_iter().filter(|test| test.name.contains(&filter));
|
|
||||||
|
|
||||||
println!("running {}/{} tests", filtered.clone().count(), TESTS.len());
|
|
||||||
let handles = filtered.map(|test| (test, std::thread::spawn(|| {
|
|
||||||
let name = test.name;
|
|
||||||
let start = std::time::SystemTime::now();
|
|
||||||
let mut proc = procspawn::spawn((), test.run);
|
|
||||||
let result = loop {
|
|
||||||
match proc.join_timeout(Duration::from_secs(10)) {
|
|
||||||
Err(e) if e.is_timeout() => {
|
|
||||||
let elapsed = start.elapsed().unwrap().as_secs();
|
|
||||||
println!("{name} has been running for {elapsed} seconds...");
|
|
||||||
|
|
||||||
if elapsed >= 30 {
|
|
||||||
println!("{name} timeout");
|
|
||||||
break Err(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
result => break result,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match result.as_ref().map_err(|e| e.panic_info()) {
|
|
||||||
Ok(Ok(_)) => println!("test {name} ... {}", "ok".green()),
|
|
||||||
Ok(Err(e)) => println!("test {name} ... {}\n {e}", "fail".red()),
|
|
||||||
Err(Some(_)) => println!("test {name} ... {}", "panic".red().underline()),
|
|
||||||
Err(None) => println!("test {name} ... {}", "error".magenta()),
|
|
||||||
}
|
|
||||||
|
|
||||||
matches!(result, Ok(Ok(())))
|
|
||||||
})));
|
|
||||||
|
|
||||||
let mut success = true;
|
|
||||||
for (_, handle) in handles {
|
|
||||||
success &= handle.join().unwrap_or(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
match success {
|
|
||||||
true => ExitCode::SUCCESS,
|
|
||||||
false => {
|
|
||||||
println!("note: use `NOCAPTURE=1` to see test output");
|
|
||||||
ExitCode::FAILURE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement an `UpdatingResolver`. Expose `SniResolver` and
|
|
||||||
// `UpdatingResolver` in a `contrib` library or as part of `rocket`.
|
|
||||||
//
|
|
||||||
// struct UpdatingResolver {
|
|
||||||
// timestamp: AtomicU64,
|
|
||||||
// config: ArcSwap<ServerConfig>
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[crate::async_trait]
|
|
||||||
// impl Resolver for UpdatingResolver {
|
|
||||||
// async fn resolve(&self, _: ClientHello<'_>) -> Option<Arc<ServerConfig>> {
|
|
||||||
// if let Either::Left(path) = self.tls_config.certs() {
|
|
||||||
// let metadata = tokio::fs::metadata(&path).await.ok()?;
|
|
||||||
// let modtime = metadata.modified().ok()?;
|
|
||||||
// let timestamp = modtime.duration_since(UNIX_EPOCH).ok()?.as_secs();
|
|
||||||
// let old_timestamp = self.timestamp.load(Ordering::Acquire);
|
|
||||||
// if timestamp > old_timestamp {
|
|
||||||
// let new_config = self.tls_config.to_server_config().await.ok()?;
|
|
||||||
// self.server_config.store(Arc::new(new_config));
|
|
||||||
// self.timestamp.store(timestamp, Ordering::Release);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Some(self.server_config.load_full())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use rocket::yansi::Paint;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Test {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub run: fn(()) -> Result<(), String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! register {
|
||||||
|
($f:ident $( ( $($v:ident: $a:expr),* ) )?) => {
|
||||||
|
::inventory::submit!($crate::Test {
|
||||||
|
name: stringify!($f $(($($v = $a),*))?),
|
||||||
|
run: |_: ()| $f($($($a),*)?).map_err(|e| e.to_string()),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory::collect!(Test);
|
||||||
|
|
||||||
|
pub fn run() -> std::process::ExitCode {
|
||||||
|
procspawn::init();
|
||||||
|
|
||||||
|
let filter = std::env::args().nth(1).unwrap_or_default();
|
||||||
|
let filtered = inventory::iter::<Test>
|
||||||
|
.into_iter()
|
||||||
|
.filter(|t| t.name.contains(&filter));
|
||||||
|
|
||||||
|
let total_tests = inventory::iter::<Test>.into_iter().count();
|
||||||
|
println!("running {}/{total_tests} tests", filtered.clone().count());
|
||||||
|
let handles = filtered.map(|test| (test, std::thread::spawn(|| {
|
||||||
|
let name = test.name;
|
||||||
|
let start = std::time::SystemTime::now();
|
||||||
|
let mut proc = procspawn::spawn((), test.run);
|
||||||
|
let result = loop {
|
||||||
|
match proc.join_timeout(Duration::from_secs(10)) {
|
||||||
|
Err(e) if e.is_timeout() => {
|
||||||
|
let elapsed = start.elapsed().unwrap().as_secs();
|
||||||
|
println!("{name} has been running for {elapsed} seconds...");
|
||||||
|
|
||||||
|
if elapsed >= 30 {
|
||||||
|
println!("{name} timeout");
|
||||||
|
break Err(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result => break result,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match result.as_ref().map_err(|e| e.panic_info()) {
|
||||||
|
Ok(Ok(_)) => println!("test {name} ... {}", "ok".green()),
|
||||||
|
Ok(Err(e)) => println!("test {name} ... {}\n {e}", "fail".red()),
|
||||||
|
Err(Some(_)) => println!("test {name} ... {}", "panic".red().underline()),
|
||||||
|
Err(None) => println!("test {name} ... {}", "error".magenta()),
|
||||||
|
}
|
||||||
|
|
||||||
|
matches!(result, Ok(Ok(())))
|
||||||
|
})));
|
||||||
|
|
||||||
|
let mut success = true;
|
||||||
|
for (_, handle) in handles {
|
||||||
|
success &= handle.join().unwrap_or(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
match success {
|
||||||
|
true => std::process::ExitCode::SUCCESS,
|
||||||
|
false => {
|
||||||
|
println!("note: use `NOCAPTURE=1` to see test output");
|
||||||
|
std::process::ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
use rocket::{tokio::net::TcpListener};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn tcp_unix_listener_fail() -> Result<()> {
|
||||||
|
use rocket::listener::unix::UnixListener;
|
||||||
|
|
||||||
|
let server = spawn! {
|
||||||
|
Rocket::default().reconfigure_with_toml("[default]\naddress = 123")
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(Error::Liftoff(stdout, _)) = server {
|
||||||
|
assert!(stdout.contains("expected: valid TCP (ip) or unix (path)"));
|
||||||
|
assert!(stdout.contains("default.address"));
|
||||||
|
} else {
|
||||||
|
panic!("unexpected result: {server:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = Server::spawn((), |(token, _)| {
|
||||||
|
let rocket = Rocket::default().reconfigure_with_toml("[default]\naddress = \"unix:foo\"");
|
||||||
|
token.launch_with::<TcpListener>(rocket)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(Error::Liftoff(stdout, _)) = server {
|
||||||
|
assert!(stdout.contains("invalid tcp endpoint: unix:foo"));
|
||||||
|
} else {
|
||||||
|
panic!("unexpected result: {server:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = Server::spawn((), |(token, _)| {
|
||||||
|
token.launch_with::<UnixListener>(Rocket::default())
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(Error::Liftoff(stdout, _)) = server {
|
||||||
|
assert!(stdout.contains("invalid unix endpoint: tcp:127.0.0.1:8000"));
|
||||||
|
} else {
|
||||||
|
panic!("unexpected result: {server:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
register!(tcp_unix_listener_fail);
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
fn test_ignite_failure() -> Result<()> {
|
||||||
|
let server = spawn! {
|
||||||
|
let fail = AdHoc::try_on_ignite("FailNow", |rocket| async { Err(rocket) });
|
||||||
|
Rocket::default().attach(fail)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(Error::Liftoff(stdout, _)) = server {
|
||||||
|
assert!(stdout.contains("ignition failure"));
|
||||||
|
assert!(stdout.contains("FailNow"));
|
||||||
|
} else {
|
||||||
|
panic!("unexpected result: {server:#?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(test_ignite_failure);
|
|
@ -0,0 +1,29 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn infinite() -> TextStream![&'static str] {
|
||||||
|
TextStream! {
|
||||||
|
loop {
|
||||||
|
yield rocket::futures::future::pending::<&str>().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_inifinite_streams_end() -> Result<()> {
|
||||||
|
let mut server = spawn! {
|
||||||
|
Rocket::default().mount("/", routes![infinite])
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
client.get(&server, "/")?.send()?;
|
||||||
|
server.terminate()?;
|
||||||
|
|
||||||
|
let stdout = server.read_stdout()?;
|
||||||
|
assert!(stdout.contains("Rocket has launched on http"));
|
||||||
|
assert!(stdout.contains("GET /"));
|
||||||
|
assert!(stdout.contains("Graceful shutdown completed"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(test_inifinite_streams_end);
|
|
@ -0,0 +1,8 @@
|
||||||
|
pub mod ignite_failure;
|
||||||
|
pub mod bind;
|
||||||
|
pub mod infinite_stream;
|
||||||
|
pub mod tls_resolver;
|
||||||
|
pub mod mtls;
|
||||||
|
pub mod sni_resolver;
|
||||||
|
pub mod tracing;
|
||||||
|
pub mod tls;
|
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
fn test_mtls(mandatory: bool) -> Result<()> {
|
||||||
|
let server = spawn!(mandatory: bool => {
|
||||||
|
let mtls_config = format!(r#"
|
||||||
|
[default.tls.mutual]
|
||||||
|
ca_certs = "{{ROCKET}}/examples/tls/private/ca_cert.pem"
|
||||||
|
mandatory = {mandatory}
|
||||||
|
"#);
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn hello(cert: rocket::mtls::Certificate<'_>) -> String {
|
||||||
|
format!("{}:{}[{}] {}", cert.serial(), cert.version(), cert.issuer(), cert.subject())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/", rank = 2)]
|
||||||
|
fn hi() -> &'static str {
|
||||||
|
"Hello!"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rocket::tls_default()
|
||||||
|
.reconfigure_with_toml(&mtls_config)
|
||||||
|
.mount("/", routes![hello, hi])
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let pem = read("{ROCKET}/examples/tls/private/client.pem")?;
|
||||||
|
let client: Client = Client::build()
|
||||||
|
.identity(reqwest::Identity::from_pem(&pem)?)
|
||||||
|
.try_into()?;
|
||||||
|
|
||||||
|
let response = client.get(&server, "/")?.send()?;
|
||||||
|
assert_eq!(response.text()?,
|
||||||
|
"611895682361338926795452113263857440769284805738:2\
|
||||||
|
[C=US, ST=CA, O=Rocket CA, CN=Rocket Root CA] \
|
||||||
|
C=US, ST=California, L=Silicon Valley, O=Rocket, \
|
||||||
|
CN=Rocket TLS Example, Email=example@rocket.local");
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
let response = client.get(&server, "/")?.send();
|
||||||
|
if mandatory {
|
||||||
|
assert!(response.unwrap_err().is_request());
|
||||||
|
} else {
|
||||||
|
assert_eq!(response?.text()?, "Hello!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(test_mtls(mandatory: true));
|
||||||
|
register!(test_mtls(mandatory: false));
|
|
@ -0,0 +1,136 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::atomic::{Ordering, AtomicUsize};
|
||||||
|
|
||||||
|
use rocket::http::uri::Host;
|
||||||
|
use rocket::tls::{Resolver, TlsConfig, ClientHello, ServerConfig};
|
||||||
|
use reqwest::tls::TlsInfo;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
static SNI_TLS_CONFIG: &str = r#"
|
||||||
|
[default.tls]
|
||||||
|
certs = "{ROCKET}/examples/tls/private/rsa_sha256_cert.pem"
|
||||||
|
key = "{ROCKET}/examples/tls/private/rsa_sha256_key.pem"
|
||||||
|
|
||||||
|
[default.tls.sni."sni1.dev"]
|
||||||
|
certs = "{ROCKET}/examples/tls/private/ecdsa_nistp256_sha256_cert.pem"
|
||||||
|
key = "{ROCKET}/examples/tls/private/ecdsa_nistp256_sha256_key_pkcs8.pem"
|
||||||
|
|
||||||
|
[default.tls.sni."sni2.dev"]
|
||||||
|
certs = "{ROCKET}/examples/tls/private/ed25519_cert.pem"
|
||||||
|
key = "{ROCKET}/examples/tls/private/ed25519_key.pem"
|
||||||
|
"#;
|
||||||
|
|
||||||
|
struct SniResolver {
|
||||||
|
default: Arc<ServerConfig>,
|
||||||
|
map: HashMap<Host<'static>, Arc<ServerConfig>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl Resolver for SniResolver {
|
||||||
|
async fn init(rocket: &Rocket<Build>) -> rocket::tls::Result<Self> {
|
||||||
|
let default: TlsConfig = rocket.figment().extract_inner("tls")?;
|
||||||
|
let sni: HashMap<Host<'_>, TlsConfig> = rocket.figment().extract_inner("tls.sni")?;
|
||||||
|
|
||||||
|
let default = Arc::new(default.server_config().await?);
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for (host, config) in sni {
|
||||||
|
let config = config.server_config().await?;
|
||||||
|
map.insert(host, Arc::new(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SniResolver { default, map })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve(&self, hello: ClientHello<'_>) -> Option<Arc<ServerConfig>> {
|
||||||
|
if let Some(Ok(host)) = hello.server_name().map(Host::parse) {
|
||||||
|
if let Some(config) = self.map.get(&host) {
|
||||||
|
return Some(config.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(self.default.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sni_resolver() -> Result<()> {
|
||||||
|
let server = spawn! {
|
||||||
|
#[get("/")] fn index() { }
|
||||||
|
|
||||||
|
Rocket::default()
|
||||||
|
.reconfigure_with_toml(SNI_TLS_CONFIG)
|
||||||
|
.mount("/", routes![index])
|
||||||
|
.attach(SniResolver::fairing())
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let client: Client = Client::build()
|
||||||
|
.resolve("unknown.dev", server.socket_addr())
|
||||||
|
.resolve("sni1.dev", server.socket_addr())
|
||||||
|
.resolve("sni2.dev", server.socket_addr())
|
||||||
|
.try_into()?;
|
||||||
|
|
||||||
|
let response = client.get(&server, "https://unknown.dev")?.send()?;
|
||||||
|
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
||||||
|
let expected = cert("{ROCKET}/examples/tls/private/rsa_sha256_cert.pem")?;
|
||||||
|
assert_eq!(tls.peer_certificate().unwrap(), expected);
|
||||||
|
|
||||||
|
let response = client.get(&server, "https://sni1.dev")?.send()?;
|
||||||
|
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
||||||
|
let expected = cert("{ROCKET}/examples/tls/private/ecdsa_nistp256_sha256_cert.pem")?;
|
||||||
|
assert_eq!(tls.peer_certificate().unwrap(), expected);
|
||||||
|
|
||||||
|
let response = client.get(&server, "https://sni2.dev")?.send()?;
|
||||||
|
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
||||||
|
let expected = cert("{ROCKET}/examples/tls/private/ed25519_cert.pem")?;
|
||||||
|
assert_eq!(tls.peer_certificate().unwrap(), expected);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CountingResolver {
|
||||||
|
config: Arc<ServerConfig>,
|
||||||
|
counter: Arc<AtomicUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl Resolver for CountingResolver {
|
||||||
|
async fn init(rocket: &Rocket<Build>) -> rocket::tls::Result<Self> {
|
||||||
|
let config: TlsConfig = rocket.figment().extract_inner("tls")?;
|
||||||
|
let config = Arc::new(config.server_config().await?);
|
||||||
|
let counter = rocket.state::<Arc<AtomicUsize>>().unwrap().clone();
|
||||||
|
Ok(Self { config, counter })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve(&self, _: ClientHello<'_>) -> Option<Arc<ServerConfig>> {
|
||||||
|
self.counter.fetch_add(1, Ordering::Release);
|
||||||
|
Some(self.config.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/count")]
|
||||||
|
fn count(counter: &State<Arc<AtomicUsize>>) -> String {
|
||||||
|
counter.load(Ordering::Acquire).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn counting_resolver() -> Result<()> {
|
||||||
|
let server = spawn! {
|
||||||
|
let counter = Arc::new(AtomicUsize::new(0));
|
||||||
|
Rocket::tls_default()
|
||||||
|
.manage(counter)
|
||||||
|
.mount("/", routes![count])
|
||||||
|
.attach(CountingResolver::fairing())
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
let response = client.get(&server, "/count")?.send()?;
|
||||||
|
assert_eq!(response.text()?, "1");
|
||||||
|
|
||||||
|
// Use a new client so we get a new TLS session.
|
||||||
|
let client = Client::default();
|
||||||
|
let response = client.get(&server, "/count")?.send()?;
|
||||||
|
assert_eq!(response.text()?, "2");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(counting_resolver);
|
||||||
|
register!(sni_resolver);
|
|
@ -0,0 +1,58 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
|
use rocket::tokio::net::TcpListener;
|
||||||
|
use rocket::{get, routes, Rocket};
|
||||||
|
use rocket::listener::Endpoint;
|
||||||
|
use rocket::tls::TlsListener;
|
||||||
|
|
||||||
|
use reqwest::tls::TlsInfo;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn hello_world(endpoint: &Endpoint) -> String {
|
||||||
|
format!("Hello, {endpoint}!")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_tls_works() -> Result<()> {
|
||||||
|
let mut server = spawn! {
|
||||||
|
Rocket::tls_default().mount("/", routes![hello_world])
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
let response = client.get(&server, "/")?.send()?;
|
||||||
|
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
||||||
|
assert!(!tls.peer_certificate().unwrap().is_empty());
|
||||||
|
assert!(response.text()?.starts_with("Hello, https://127.0.0.1"));
|
||||||
|
|
||||||
|
server.terminate()?;
|
||||||
|
let stdout = server.read_stdout()?;
|
||||||
|
assert!(stdout.contains("Rocket has launched on https"));
|
||||||
|
assert!(stdout.contains("Graceful shutdown completed"));
|
||||||
|
assert!(stdout.contains("GET /"));
|
||||||
|
|
||||||
|
let server = Server::spawn((), |(token, _)| {
|
||||||
|
let rocket = rocket::build()
|
||||||
|
.reconfigure_with_toml(TLS_CONFIG)
|
||||||
|
.mount("/", routes![hello_world]);
|
||||||
|
|
||||||
|
token.with_launch(rocket, |rocket| {
|
||||||
|
let config = rocket.figment().extract_inner("tls");
|
||||||
|
rocket.try_launch_on(async move {
|
||||||
|
let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 0);
|
||||||
|
let listener = TcpListener::bind(addr).await?;
|
||||||
|
TlsListener::from(listener, config?).await
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
let response = client.get(&server, "/")?.send()?;
|
||||||
|
let tls = response.extensions().get::<TlsInfo>().unwrap();
|
||||||
|
assert!(!tls.peer_certificate().unwrap().is_empty());
|
||||||
|
assert!(response.text()?.starts_with("Hello, https://127.0.0.1"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(test_tls_works);
|
|
@ -0,0 +1,80 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use rocket::tls::{ClientHello, Resolver, ServerConfig, TlsConfig};
|
||||||
|
|
||||||
|
struct CountingResolver {
|
||||||
|
config: Arc<ServerConfig>,
|
||||||
|
counter: Arc<AtomicUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl Resolver for CountingResolver {
|
||||||
|
async fn init(rocket: &Rocket<Build>) -> rocket::tls::Result<Self> {
|
||||||
|
let config: TlsConfig = rocket.figment().extract_inner("tls")?;
|
||||||
|
let config = Arc::new(config.server_config().await?);
|
||||||
|
let counter = rocket.state::<Arc<AtomicUsize>>().unwrap().clone();
|
||||||
|
Ok(Self { config, counter })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve(&self, _: ClientHello<'_>) -> Option<Arc<ServerConfig>> {
|
||||||
|
self.counter.fetch_add(1, Ordering::Release);
|
||||||
|
Some(self.config.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/count")]
|
||||||
|
fn count(counter: &State<Arc<AtomicUsize>>) -> String {
|
||||||
|
counter.load(Ordering::Acquire).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_tls_resolver() -> Result<()> {
|
||||||
|
let server = spawn! {
|
||||||
|
let counter = Arc::new(AtomicUsize::new(0));
|
||||||
|
Rocket::tls_default()
|
||||||
|
.manage(counter)
|
||||||
|
.mount("/", routes![count])
|
||||||
|
.attach(CountingResolver::fairing())
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
let response = client.get(&server, "/count")?.send()?;
|
||||||
|
assert_eq!(response.text()?, "1");
|
||||||
|
|
||||||
|
// Use a new client so we get a new TLS session.
|
||||||
|
let client = Client::default();
|
||||||
|
let response = client.get(&server, "/count")?.send()?;
|
||||||
|
assert_eq!(response.text()?, "2");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(test_tls_resolver);
|
||||||
|
|
||||||
|
// TODO: Implement an `UpdatingResolver`. Expose `SniResolver` and
|
||||||
|
// `UpdatingResolver` in a `contrib` library or as part of `rocket`.
|
||||||
|
//
|
||||||
|
// struct UpdatingResolver {
|
||||||
|
// timestamp: AtomicU64,
|
||||||
|
// config: ArcSwap<ServerConfig>
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[crate::async_trait]
|
||||||
|
// impl Resolver for UpdatingResolver {
|
||||||
|
// async fn resolve(&self, _: ClientHello<'_>) -> Option<Arc<ServerConfig>> {
|
||||||
|
// if let Either::Left(path) = self.tls_config.certs() {
|
||||||
|
// let metadata = tokio::fs::metadata(&path).await.ok()?;
|
||||||
|
// let modtime = metadata.modified().ok()?;
|
||||||
|
// let timestamp = modtime.duration_since(UNIX_EPOCH).ok()?.as_secs();
|
||||||
|
// let old_timestamp = self.timestamp.load(Ordering::Acquire);
|
||||||
|
// if timestamp > old_timestamp {
|
||||||
|
// let new_config = self.tls_config.to_server_config().await.ok()?;
|
||||||
|
// self.server_config.store(Arc::new(new_config));
|
||||||
|
// self.timestamp.store(timestamp, Ordering::Release);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Some(self.server_config.load_full())
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -0,0 +1,123 @@
|
||||||
|
//! Check that guard failures result in trace with `Display` message for guard
|
||||||
|
//! types that implement `Display` and otherwise uses `Debug`.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::data::{self, FromData};
|
||||||
|
use rocket::http::uri::{Segments, fmt::Path};
|
||||||
|
use rocket::request::{self, FromParam, FromRequest, FromSegments};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct UseDisplay(&'static str);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct UseDebug;
|
||||||
|
|
||||||
|
impl fmt::Display for UseDisplay {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "this is the display impl: {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromParam<'_> for UseDisplay {
|
||||||
|
type Error = Self;
|
||||||
|
fn from_param(_: &str) -> Result<Self, Self::Error> { Err(Self("param")) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromParam<'_> for UseDebug {
|
||||||
|
type Error = Self;
|
||||||
|
fn from_param(_: &str) -> Result<Self, Self::Error> { Err(Self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for UseDisplay {
|
||||||
|
type Error = Self;
|
||||||
|
async fn from_request(_: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
request::Outcome::Error((Status::InternalServerError, Self("req")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for UseDebug {
|
||||||
|
type Error = Self;
|
||||||
|
async fn from_request(_: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
request::Outcome::Error((Status::InternalServerError, Self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromData<'r> for UseDisplay {
|
||||||
|
type Error = Self;
|
||||||
|
async fn from_data(_: &'r Request<'_>, _: Data<'r>) -> data::Outcome<'r, Self> {
|
||||||
|
data::Outcome::Error((Status::InternalServerError, Self("data")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromData<'r> for UseDebug {
|
||||||
|
type Error = Self;
|
||||||
|
async fn from_data(_: &'r Request<'_>, _: Data<'r>) -> data::Outcome<'r, Self> {
|
||||||
|
data::Outcome::Error((Status::InternalServerError, Self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> FromSegments<'r> for UseDisplay {
|
||||||
|
type Error = Self;
|
||||||
|
fn from_segments(_: Segments<'r, Path>) -> Result<Self, Self::Error> { Err(Self("segment")) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> FromSegments<'r> for UseDebug {
|
||||||
|
type Error = Self;
|
||||||
|
fn from_segments(_: Segments<'r, Path>) -> Result<Self, Self::Error> { Err(Self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_display_guard_err() -> Result<()> {
|
||||||
|
#[get("/<_v>", rank = 1)] fn a(_v: UseDisplay) {}
|
||||||
|
#[get("/<_v..>", rank = 2)] fn b(_v: UseDisplay) {}
|
||||||
|
#[get("/<_..>", rank = 3)] fn d(_v: UseDisplay) {}
|
||||||
|
#[post("/<_..>", data = "<_v>")] fn c(_v: UseDisplay) {}
|
||||||
|
|
||||||
|
let mut server = spawn! {
|
||||||
|
Rocket::default().mount("/", routes![a, b, c, d])
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
client.get(&server, "/foo")?.send()?;
|
||||||
|
client.post(&server, "/foo")?.send()?;
|
||||||
|
server.terminate()?;
|
||||||
|
|
||||||
|
let stdout = server.read_stdout()?;
|
||||||
|
assert!(stdout.contains("this is the display impl: param"));
|
||||||
|
assert!(stdout.contains("this is the display impl: req"));
|
||||||
|
assert!(stdout.contains("this is the display impl: segment"));
|
||||||
|
assert!(stdout.contains("this is the display impl: data"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_debug_guard_err() -> Result<()> {
|
||||||
|
#[get("/<_v>", rank = 1)] fn a(_v: UseDebug) {}
|
||||||
|
#[get("/<_v..>", rank = 2)] fn b(_v: UseDebug) {}
|
||||||
|
#[get("/<_..>", rank = 3)] fn d(_v: UseDebug) {}
|
||||||
|
#[post("/<_..>", data = "<_v>")] fn c(_v: UseDebug) {}
|
||||||
|
|
||||||
|
let mut server = spawn! {
|
||||||
|
Rocket::default().mount("/", routes![a, b, c, d])
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let client = Client::default();
|
||||||
|
client.get(&server, "/foo")?.send()?;
|
||||||
|
client.post(&server, "/foo")?.send()?;
|
||||||
|
server.terminate()?;
|
||||||
|
|
||||||
|
let stdout = server.read_stdout()?;
|
||||||
|
assert!(!stdout.contains("this is the display impl"));
|
||||||
|
assert!(stdout.contains("UseDebug"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(test_display_guard_err);
|
||||||
|
register!(test_debug_guard_err);
|
Loading…
Reference in New Issue