rocket/
error.rs

1//! Types representing various errors that can occur in a Rocket application.
2
3use std::{io, fmt};
4use std::sync::{Arc, atomic::{Ordering, AtomicBool}};
5use std::error::Error as StdError;
6
7use yansi::Paint;
8use figment::Profile;
9
10use crate::{Rocket, Orbit};
11
12/// An error that occurs during launch.
13///
14/// An `Error` is returned by [`launch()`](Rocket::launch()) when launching an
15/// application fails or, more rarely, when the runtime fails after launching.
16///
17/// # Panics
18///
19/// A value of this type panics if it is dropped without first being inspected.
20/// An _inspection_ occurs when any method is called. For instance, if
21/// `println!("Error: {}", e)` is called, where `e: Error`, the `Display::fmt`
22/// method being called by `println!` results in `e` being marked as inspected;
23/// a subsequent `drop` of the value will _not_ result in a panic. The following
24/// snippet illustrates this:
25///
26/// ```rust
27/// # let _ = async {
28/// if let Err(error) = rocket::build().launch().await {
29///     // This println "inspects" the error.
30///     println!("Launch failed! Error: {}", error);
31///
32///     // This call to drop (explicit here for demonstration) will do nothing.
33///     drop(error);
34/// }
35/// # };
36/// ```
37///
38/// When a value of this type panics, the corresponding error message is pretty
39/// printed to the console. The following illustrates this:
40///
41/// ```rust
42/// # let _ = async {
43/// let error = rocket::build().launch().await;
44///
45/// // This call to drop (explicit here for demonstration) will result in
46/// // `error` being pretty-printed to the console along with a `panic!`.
47/// drop(error);
48/// # };
49/// ```
50///
51/// # Usage
52///
53/// An `Error` value should usually be allowed to `drop` without inspection.
54/// There are at least two exceptions:
55///
56///   1. If you are writing a library or high-level application on-top of
57///      Rocket, you likely want to inspect the value before it drops to avoid a
58///      Rocket-specific `panic!`. This typically means simply printing the
59///      value.
60///
61///   2. You want to display your own error messages.
62pub struct Error {
63    handled: AtomicBool,
64    kind: ErrorKind
65}
66
67/// The kind error that occurred.
68///
69/// In almost every instance, a launch error occurs because of an I/O error;
70/// this is represented by the `Io` variant. A launch error may also occur
71/// because of ill-defined routes that lead to collisions or because a fairing
72/// encountered an error; these are represented by the `Collision` and
73/// `FailedFairing` variants, respectively.
74#[derive(Debug)]
75#[non_exhaustive]
76pub enum ErrorKind {
77    /// Binding to the provided address/port failed.
78    Bind(io::Error),
79    /// An I/O error occurred during launch.
80    Io(io::Error),
81    /// A valid [`Config`](crate::Config) could not be extracted from the
82    /// configured figment.
83    Config(figment::Error),
84    /// Route collisions were detected.
85    Collisions(crate::router::Collisions),
86    /// Launch fairing(s) failed.
87    FailedFairings(Vec<crate::fairing::Info>),
88    /// Sentinels requested abort.
89    SentinelAborts(Vec<crate::sentinel::Sentry>),
90    /// The configuration profile is not debug but not secret key is configured.
91    InsecureSecretKey(Profile),
92    /// Shutdown failed.
93    Shutdown(
94        /// The instance of Rocket that failed to shutdown.
95        Arc<Rocket<Orbit>>,
96        /// The error that occurred during shutdown, if any.
97        Option<Box<dyn StdError + Send + Sync>>
98    ),
99}
100
101impl From<ErrorKind> for Error {
102    fn from(kind: ErrorKind) -> Self {
103        Error::new(kind)
104    }
105}
106
107impl Error {
108    #[inline(always)]
109    pub(crate) fn new(kind: ErrorKind) -> Error {
110        Error { handled: AtomicBool::new(false), kind }
111    }
112
113    #[inline(always)]
114    pub(crate) fn shutdown<E>(rocket: Arc<Rocket<Orbit>>, error: E) -> Error
115        where E: Into<Option<crate::http::hyper::Error>>
116    {
117        let error = error.into().map(|e| Box::new(e) as Box<dyn StdError + Sync + Send>);
118        Error::new(ErrorKind::Shutdown(rocket, error))
119    }
120
121    #[inline(always)]
122    fn was_handled(&self) -> bool {
123        self.handled.load(Ordering::Acquire)
124    }
125
126    #[inline(always)]
127    fn mark_handled(&self) {
128        self.handled.store(true, Ordering::Release)
129    }
130
131    /// Retrieve the `kind` of the launch error.
132    ///
133    /// # Example
134    ///
135    /// ```rust
136    /// use rocket::error::ErrorKind;
137    ///
138    /// # let _ = async {
139    /// if let Err(error) = rocket::build().launch().await {
140    ///     match error.kind() {
141    ///         ErrorKind::Io(e) => println!("found an i/o launch error: {}", e),
142    ///         e => println!("something else happened: {}", e)
143    ///     }
144    /// }
145    /// # };
146    /// ```
147    #[inline]
148    pub fn kind(&self) -> &ErrorKind {
149        self.mark_handled();
150        &self.kind
151    }
152
153    /// Prints the error with color (if enabled) and detail. Returns a string
154    /// that indicates the abort condition such as "aborting due to i/o error".
155    ///
156    /// This function is called on `Drop` to display the error message. By
157    /// contrast, the `Display` implementation prints a succinct version of the
158    /// error, without detail.
159    ///
160    /// ```rust
161    /// # let _ = async {
162    /// if let Err(error) = rocket::build().launch().await {
163    ///     let abort = error.pretty_print();
164    ///     panic!("{}", abort);
165    /// }
166    /// # };
167    /// ```
168    pub fn pretty_print(&self) -> &'static str {
169        self.mark_handled();
170        match self.kind() {
171            ErrorKind::Bind(ref e) => {
172                error!("Rocket failed to bind network socket to given address/port.");
173                info_!("{}", e);
174                "aborting due to socket bind error"
175            }
176            ErrorKind::Io(ref e) => {
177                error!("Rocket failed to launch due to an I/O error.");
178                info_!("{}", e);
179                "aborting due to i/o error"
180            }
181            ErrorKind::Collisions(ref collisions) => {
182                fn log_collisions<T: fmt::Display>(kind: &str, collisions: &[(T, T)]) {
183                    if collisions.is_empty() { return }
184
185                    error!("Rocket failed to launch due to the following {} collisions:", kind);
186                    for &(ref a, ref b) in collisions {
187                        info_!("{} {} {}", a, "collides with".red().italic(), b)
188                    }
189                }
190
191                log_collisions("route", &collisions.routes);
192                log_collisions("catcher", &collisions.catchers);
193
194                info_!("Note: Route collisions can usually be resolved by ranking routes.");
195                "aborting due to detected routing collisions"
196            }
197            ErrorKind::FailedFairings(ref failures) => {
198                error!("Rocket failed to launch due to failing fairings:");
199                for fairing in failures {
200                    info_!("{}", fairing.name);
201                }
202
203                "aborting due to fairing failure(s)"
204            }
205            ErrorKind::InsecureSecretKey(profile) => {
206                error!("secrets enabled in non-debug without `secret_key`");
207                info_!("selected profile: {}", profile.primary().bold());
208                info_!("disable `secrets` feature or configure a `secret_key`");
209                "aborting due to insecure configuration"
210            }
211            ErrorKind::Config(error) => {
212                crate::config::pretty_print_error(error.clone());
213                "aborting due to invalid configuration"
214            }
215            ErrorKind::SentinelAborts(ref errors) => {
216                error!("Rocket failed to launch due to aborting sentinels:");
217                for sentry in errors {
218                    let name = sentry.type_name.primary().bold();
219                    let (file, line, col) = sentry.location;
220                    info_!("{} ({}:{}:{})", name, file, line, col);
221                }
222
223                "aborting due to sentinel-triggered abort(s)"
224            }
225            ErrorKind::Shutdown(_, error) => {
226                error!("Rocket failed to shutdown gracefully.");
227                if let Some(e) = error {
228                    info_!("{}", e);
229                }
230
231                "aborting due to failed shutdown"
232            }
233        }
234    }
235}
236
237impl std::error::Error for Error {  }
238
239impl fmt::Display for ErrorKind {
240    #[inline]
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        match self {
243            ErrorKind::Bind(e) => write!(f, "binding failed: {}", e),
244            ErrorKind::Io(e) => write!(f, "I/O error: {}", e),
245            ErrorKind::Collisions(_) => "collisions detected".fmt(f),
246            ErrorKind::FailedFairings(_) => "launch fairing(s) failed".fmt(f),
247            ErrorKind::InsecureSecretKey(_) => "insecure secret key config".fmt(f),
248            ErrorKind::Config(_) => "failed to extract configuration".fmt(f),
249            ErrorKind::SentinelAborts(_) => "sentinel(s) aborted".fmt(f),
250            ErrorKind::Shutdown(_, Some(e)) => write!(f, "shutdown failed: {}", e),
251            ErrorKind::Shutdown(_, None) => "shutdown failed".fmt(f),
252        }
253    }
254}
255
256impl fmt::Debug for Error {
257    #[inline]
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        self.mark_handled();
260        self.kind().fmt(f)
261    }
262}
263
264impl fmt::Display for Error {
265    #[inline]
266    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267        self.mark_handled();
268        write!(f, "{}", self.kind())
269    }
270}
271
272impl Drop for Error {
273    fn drop(&mut self) {
274        // Don't panic if the message has been seen. Don't double-panic.
275        if self.was_handled() || std::thread::panicking() {
276            return
277        }
278
279        panic!("{}", self.pretty_print());
280    }
281}