1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
//! Any errors that merino-web might generate, and supporting implementations.
//!
//! This module implements the supporting functionalities to manipulate
//! [crate::error::HandlerError] to make it esier to send them to Sentry.
//! The [crate::error::HandlerError] wraps the internal error [crate::error::HandlerErrorKind]
//! and a related backtrace.
//! The corresponding backtrace is captured when the error is created.
//! This happens automatically when a [crate::error::HandlerErrorKind] is converted into a [crate::error::HandlerError].
//!
//! Developers are expected to use [crate::error::HandlerError] as the error type of
//! their functions and to set the appropriate error by
//! * explicitly converting it using `into()`, e.g. `Err(HandlerErrorKind::Internal.into())`,
//! * implicitly converting it using the question mark operator, e.g. `Err(HandleErrorKind::Interal)?`.
//!
//! New errors can be added by extending [crate::error::HandlerErrorKind].
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use actix_web::{http::StatusCode, HttpResponse, ResponseError};
use backtrace::Backtrace;
use serde_json::Value;
use thiserror::Error;
/// The Standard Error for most of Merino
pub struct HandlerError {
// Important: please make sure to update the implementation of
// std::fmt::Debug for this struct if new fields are added here.
/// The wrapped error value.
kind: HandlerErrorKind,
/// The backtrace related to the wrapped error.
pub(crate) backtrace: Backtrace,
}
/// An error that happened in a web handler.
#[derive(Error, Debug)]
pub enum HandlerErrorKind {
/// A generic error, when there is nothing more specific to say.
#[error("Internal error")]
Internal,
}
impl From<HandlerErrorKind> for actix_web::Error {
fn from(kind: HandlerErrorKind) -> Self {
let error: HandlerError = kind.into();
error.into()
}
}
impl HandlerError {
/// Access the wrapped error.
pub fn kind(&self) -> &HandlerErrorKind {
&self.kind
}
/// Get an `HandlerError` representing an `Internal` error.
///
/// This is a convenience function: the same result can be
/// achieved by directly using `HandlerErrorKind::Internal.into()`.
pub fn internal() -> Self {
HandlerErrorKind::Internal.into()
}
}
impl Error for HandlerError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.kind.source()
}
}
impl<T> From<T> for HandlerError
where
HandlerErrorKind: From<T>,
{
fn from(item: T) -> Self {
HandlerError {
kind: HandlerErrorKind::from(item),
backtrace: Backtrace::new(),
}
}
}
impl fmt::Display for HandlerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.kind.fmt(f)
}
}
impl std::fmt::Debug for HandlerError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
// Sentry will scan the printed debug information for `HandlerError`
// to determine the "event type" to display and to group events by:
// to make sure different errors don't get grouped together, we format
// the name of this debug struct as `HandlerError/<error name>`.
// See `sentry::parse_type_from_debug` used by middleware/sentry.rs
fmt.debug_struct(&format!("HandlerError/{:?}", &self.kind))
.field("kind", &self.kind)
.field("backtrace", &self.backtrace)
.finish()
}
}
impl ResponseError for HandlerError {
/// Convert the error to an HTTP status code.
fn status_code(&self) -> StatusCode {
match self.kind() {
HandlerErrorKind::Internal => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let mut response = HashMap::new();
response.insert(
"error".to_owned(),
Value::String(format!("{}", self.kind())),
);
HttpResponse::InternalServerError().json(response)
}
}