use std::error::Error as StdError;
use std::sync::Arc;
use std::{fmt, io, process};
use figment::Profile;
use crate::listener::Endpoint;
use crate::trace::Trace;
use crate::{Catcher, Ignite, Orbit, Phase, Rocket, Route};
pub struct Error {
pub(crate) kind: ErrorKind,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ErrorKind {
Bind(Option<Endpoint>, Box<dyn StdError + Send>),
Io(io::Error),
Config(figment::Error),
Collisions {
routes: Vec<(Route, Route)>,
catchers: Vec<(Catcher, Catcher)>,
},
FailedFairings(Vec<crate::fairing::Info>),
SentinelAborts(Vec<crate::sentinel::Sentry>),
InsecureSecretKey(Profile),
Liftoff(
Result<Box<Rocket<Ignite>>, Arc<Rocket<Orbit>>>,
tokio::task::JoinError,
),
Shutdown(Arc<Rocket<Orbit>>),
}
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Empty;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct InvalidOption<'a> {
pub value: &'a str,
pub options: &'static [&'static str],
}
impl<'a> InvalidOption<'a> {
#[doc(hidden)]
pub fn new(value: &'a str, options: &'static [&'static str]) -> Self {
Self { value, options }
}
}
impl fmt::Display for InvalidOption<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"unexpected value {:?}, expected one of {:?}",
self.value, self.options
)
}
}
impl std::error::Error for InvalidOption<'_> {}
impl Error {
#[inline(always)]
pub(crate) fn new(kind: ErrorKind) -> Error {
Error { kind }
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn report<P: Phase>(result: Result<Rocket<P>, Error>) -> process::ExitCode {
match result {
Ok(_) => process::ExitCode::SUCCESS,
Err(e) => {
span_error!("launch failure", "aborting launch due to error" => e.trace_error());
process::ExitCode::SUCCESS
}
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Error::new(kind)
}
}
impl From<figment::Error> for Error {
fn from(e: figment::Error) -> Self {
Error::new(ErrorKind::Config(e))
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::new(ErrorKind::Io(e))
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match &self.kind {
ErrorKind::Bind(_, e) => Some(&**e),
ErrorKind::Io(e) => Some(e),
ErrorKind::Collisions { .. } => None,
ErrorKind::FailedFairings(_) => None,
ErrorKind::InsecureSecretKey(_) => None,
ErrorKind::Config(e) => Some(e),
ErrorKind::SentinelAborts(_) => None,
ErrorKind::Liftoff(_, e) => Some(e),
ErrorKind::Shutdown(_) => None,
}
}
}
impl fmt::Display for ErrorKind {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::Bind(_, e) => write!(f, "binding failed: {e}"),
ErrorKind::Io(e) => write!(f, "I/O error: {e}"),
ErrorKind::Collisions { .. } => "collisions detected".fmt(f),
ErrorKind::FailedFairings(_) => "launch fairing(s) failed".fmt(f),
ErrorKind::InsecureSecretKey(_) => "insecure secret key config".fmt(f),
ErrorKind::Config(_) => "failed to extract configuration".fmt(f),
ErrorKind::SentinelAborts(_) => "sentinel(s) aborted".fmt(f),
ErrorKind::Liftoff(_, _) => "liftoff failed".fmt(f),
ErrorKind::Shutdown(_) => "shutdown failed".fmt(f),
}
}
}
impl fmt::Debug for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.kind.fmt(f)
}
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
impl fmt::Debug for Empty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("empty parameter")
}
}
impl fmt::Display for Empty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("empty parameter")
}
}
impl StdError for Empty {}
struct ServerError<'a>(&'a (dyn StdError + 'static));
impl fmt::Display for ServerError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let error = &self.0;
if let Some(e) = error.downcast_ref::<hyper::Error>() {
write!(f, "request failed: {e}")?;
} else if let Some(e) = error.downcast_ref::<io::Error>() {
write!(f, "connection error: ")?;
match e.kind() {
io::ErrorKind::NotConnected => write!(f, "remote disconnected")?,
io::ErrorKind::UnexpectedEof => write!(f, "remote sent early eof")?,
io::ErrorKind::ConnectionReset | io::ErrorKind::ConnectionAborted => {
write!(f, "terminated by remote")?
}
_ => write!(f, "{e}")?,
}
} else {
write!(f, "http server error: {error}")?;
}
Ok(())
}
}
#[track_caller]
pub(crate) fn log_server_error(error: &(dyn StdError + 'static)) {
let mut error: &(dyn StdError + 'static) = error;
if error.downcast_ref::<hyper::Error>().is_some() {
span_warn!("request error", "{}", ServerError(error) => {
while let Some(source) = error.source() {
error = source;
warn!("{}", ServerError(error));
}
});
} else {
span_error!("server error", "{}", ServerError(error) => {
while let Some(source) = error.source() {
error = source;
error!("{}", ServerError(error));
}
});
}
}
#[doc(hidden)]
pub mod display_hack_impl {
use super::*;
use crate::util::Formatter;
pub struct DisplayHack<T: ?Sized>(pub T);
pub trait DefaultDisplay {
fn display(&self) -> impl fmt::Display;
}
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))
}
}
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::{DefaultDisplay as _, DisplayHack};
#[allow(unreachable_code)]
DisplayHack($v).display()
}};
}
#[doc(hidden)]
pub use display_hack;