Improve internal server errors significantly

This commit is contained in:
Brian Picciano 2023-07-08 16:18:45 +02:00
parent a3c823c7b2
commit 9d44593e73
2 changed files with 43 additions and 24 deletions

View File

@ -3,6 +3,7 @@ pub mod checker;
pub mod config; pub mod config;
pub mod manager; pub mod manager;
use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@ -21,6 +22,12 @@ impl Name {
} }
} }
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.utf8_str)
}
}
impl FromStr for Name { impl FromStr for Name {
type Err = <trust_dns_rr::Name as FromStr>::Err; type Err = <trust_dns_rr::Name as FromStr>::Err;

View File

@ -91,7 +91,7 @@ struct DomainSyncArgs {
} }
impl<'svc> Service { impl<'svc> Service {
fn serve(&self, status_code: u16, path: &'_ str, body: Body) -> Response<Body> { fn serve(&self, status_code: u16, path: &str, body: Body) -> Response<Body> {
let content_type = mime_guess::from_path(path) let content_type = mime_guess::from_path(path)
.first_or_octet_stream() .first_or_octet_stream()
.to_string(); .to_string();
@ -103,20 +103,19 @@ impl<'svc> Service {
{ {
Ok(res) => res, Ok(res) => res,
Err(err) => { Err(err) => {
// if the status code was already a 500, don't try to render _another_ 500, it'll
// probably fail for the same reason. At this point something is incredibly wrong,
// just panic.
if status_code == 500 { if status_code == 500 {
panic!("failed to build {}: {}", path, err); panic!("failed to build {}: {}", path, err);
} }
self.serve( self.internal_error(format!("failed to build {}: {}", path, err).as_str())
500,
"error.txt",
format!("failed to build {}: {}", path, err).into(),
)
} }
} }
} }
fn render<T>(&self, status_code: u16, name: &'_ str, value: T) -> Response<Body> fn render<T>(&self, status_code: u16, name: &str, value: T) -> Response<Body>
where where
T: Serialize, T: Serialize,
{ {
@ -134,7 +133,7 @@ impl<'svc> Service {
self.serve(status_code, name, rendered.into()) self.serve(status_code, name, rendered.into())
} }
fn render_error_page(&self, status_code: u16, e: &'_ str) -> Response<Body> { fn render_error_page(&self, status_code: u16, e: &str) -> Response<Body> {
#[derive(Serialize)] #[derive(Serialize)]
struct Response<'a> { struct Response<'a> {
error_msg: &'a str, error_msg: &'a str,
@ -150,7 +149,15 @@ impl<'svc> Service {
) )
} }
fn render_page<T>(&self, name: &'_ str, data: T) -> Response<Body> fn internal_error(&self, e: &str) -> Response<Body> {
log::error!("Internal error: {e}");
self.render_error_page(
500,
"An unexpected error occurred. The server administrator will be able to help.",
)
}
fn render_page<T>(&self, name: &str, data: T) -> Response<Body>
where where
T: Serialize, T: Serialize,
{ {
@ -164,7 +171,7 @@ impl<'svc> Service {
) )
} }
fn serve_origin(&self, domain: domain::Name, path: &'_ str) -> Response<Body> { fn serve_origin(&self, domain: domain::Name, path: &str) -> Response<Body> {
let mut path_owned; let mut path_owned;
let path = match path.ends_with('/') { let path = match path.ends_with('/') {
@ -185,7 +192,7 @@ impl<'svc> Service {
self.render_error_page(404, "File not found") self.render_error_page(404, "File not found")
} }
Err(domain::manager::GetFileError::Unexpected(e)) => { Err(domain::manager::GetFileError::Unexpected(e)) => {
self.render_error_page(500, format!("failed to fetch file {path}: {e}").as_str()) self.internal_error(format!("failed to fetch file {path}: {e}").as_str())
} }
} }
} }
@ -199,11 +206,9 @@ impl<'svc> Service {
let query = req.uri().query().unwrap_or(""); let query = req.uri().query().unwrap_or("");
match serde_urlencoded::from_str::<In>(query) { match serde_urlencoded::from_str::<In>(query) {
Ok(args) => f(args).await, Ok(args) => f(args).await,
Err(err) => self.serve( Err(err) => {
400, self.render_error_page(400, format!("failed to parse query args: {}", err).as_str())
"error.txt", }
format!("failed to parse query args: {}", err).into(),
),
} }
} }
@ -218,8 +223,13 @@ impl<'svc> Service {
Ok(config) => Some(config), Ok(config) => Some(config),
Err(domain::manager::GetConfigError::NotFound) => None, Err(domain::manager::GetConfigError::NotFound) => None,
Err(domain::manager::GetConfigError::Unexpected(e)) => { Err(domain::manager::GetConfigError::Unexpected(e)) => {
return self return self.internal_error(
.render_error_page(500, format!("retrieving configuration: {}", e).as_str()); format!(
"retrieving configuration for domain {}: {}",
&args.domain, e
)
.as_str(),
);
} }
}; };
@ -256,8 +266,9 @@ impl<'svc> Service {
let config_hash = match config.hash() { let config_hash = match config.hash() {
Ok(hash) => hash, Ok(hash) => hash,
Err(e) => { Err(e) => {
return self return self.internal_error(
.render_error_page(500, format!("failed to hash domain config: {e}").as_str()) format!("failed to hash domain config {config:?}: {e}").as_str(),
)
} }
}; };
@ -326,9 +337,7 @@ impl<'svc> Service {
let domains = match self.domain_manager.all_domains() { let domains = match self.domain_manager.all_domains() {
Ok(domains) => domains, Ok(domains) => domains,
Err(e) => { Err(e) => return self.internal_error(format!("failed get all domains: {e}").as_str()),
return self.render_error_page(500, format!("failed get all domains: {e}").as_str())
}
}; };
let mut domains: Vec<String> = domains let mut domains: Vec<String> = domains
@ -406,7 +415,10 @@ impl<'svc> Service {
.await .await
} }
(&Method::GET, "/domains.html") => self.domains(), (&Method::GET, "/domains.html") => self.domains(),
_ => self.render_error_page(404, "Page not found!"), _ => self.render_error_page(
404,
"This is not the page you're looking for. This page doesn't even exist!",
),
} }
} }
} }