use eth2::types::{ErrorMessage, Failure, IndexedErrorMessage}; use std::convert::Infallible; use std::error::Error; use std::fmt; use std::fmt::Debug; use warp::{Reply, http::StatusCode, reject::Reject, reply::Response}; #[derive(Debug)] pub struct ServerSentEventError(pub String); impl Error for ServerSentEventError {} impl fmt::Display for ServerSentEventError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } pub fn server_sent_event_error(s: String) -> ServerSentEventError { ServerSentEventError(s) } #[derive(Debug)] pub struct BeaconStateError(pub types::BeaconStateError); impl Reject for BeaconStateError {} pub fn beacon_state_error(e: types::BeaconStateError) -> warp::reject::Rejection { warp::reject::custom(BeaconStateError(e)) } #[derive(Debug)] pub struct ArithError(pub safe_arith::ArithError); impl Reject for ArithError {} pub fn arith_error(e: safe_arith::ArithError) -> warp::reject::Rejection { warp::reject::custom(ArithError(e)) } #[derive(Debug)] pub struct UnhandledError(pub Box); impl Reject for UnhandledError {} pub fn unhandled_error(e: D) -> warp::reject::Rejection { warp::reject::custom(UnhandledError(Box::new(e))) } #[derive(Debug)] pub struct CustomNotFound(pub String); impl Reject for CustomNotFound {} pub fn custom_not_found(msg: String) -> warp::reject::Rejection { warp::reject::custom(CustomNotFound(msg)) } #[derive(Debug)] pub struct CustomBadRequest(pub String); impl Reject for CustomBadRequest {} pub fn custom_bad_request(msg: String) -> warp::reject::Rejection { warp::reject::custom(CustomBadRequest(msg)) } #[derive(Debug)] pub struct CustomDeserializeError(pub String); impl Reject for CustomDeserializeError {} pub fn custom_deserialize_error(msg: String) -> warp::reject::Rejection { warp::reject::custom(CustomDeserializeError(msg)) } #[derive(Debug)] pub struct CustomServerError(pub String); impl Reject for CustomServerError {} pub fn custom_server_error(msg: String) -> warp::reject::Rejection { warp::reject::custom(CustomServerError(msg)) } #[derive(Debug)] pub struct BroadcastWithoutImport(pub String); impl Reject for BroadcastWithoutImport {} pub fn broadcast_without_import(msg: String) -> warp::reject::Rejection { warp::reject::custom(BroadcastWithoutImport(msg)) } #[derive(Debug)] pub struct ObjectInvalid(pub String); impl Reject for ObjectInvalid {} pub fn object_invalid(msg: String) -> warp::reject::Rejection { warp::reject::custom(ObjectInvalid(msg)) } #[derive(Debug)] pub struct NotSynced(pub String); impl Reject for NotSynced {} pub fn not_synced(msg: String) -> warp::reject::Rejection { warp::reject::custom(NotSynced(msg)) } #[derive(Debug)] pub struct InvalidAuthorization(pub String); impl Reject for InvalidAuthorization {} pub fn invalid_auth(msg: String) -> warp::reject::Rejection { warp::reject::custom(InvalidAuthorization(msg)) } #[derive(Debug)] pub struct UnsupportedMediaType(pub String); impl Reject for UnsupportedMediaType {} pub fn unsupported_media_type(msg: String) -> warp::reject::Rejection { warp::reject::custom(UnsupportedMediaType(msg)) } #[derive(Debug)] pub struct IndexedBadRequestErrors { pub message: String, pub failures: Vec, } impl Reject for IndexedBadRequestErrors {} pub fn indexed_bad_request(message: String, failures: Vec) -> warp::reject::Rejection { warp::reject::custom(IndexedBadRequestErrors { message, failures }) } /// This function receives a `Rejection` and tries to return a custom /// value, otherwise simply passes the rejection along. pub async fn handle_rejection(err: warp::Rejection) -> Result { let code; let message; if let Some(e) = err.find::() { message = format!("BAD_REQUEST: {}", e.message); code = StatusCode::BAD_REQUEST; let json = warp::reply::json(&IndexedErrorMessage { code: code.as_u16(), message, failures: e.failures.clone(), }); return Ok(warp::reply::with_status(json, code)); } if err.is_not_found() { code = StatusCode::NOT_FOUND; message = "NOT_FOUND".to_string(); } else if err.find::().is_some() { code = StatusCode::UNSUPPORTED_MEDIA_TYPE; message = "UNSUPPORTED_MEDIA_TYPE".to_string(); } else if let Some(e) = err.find::() { message = format!("BAD_REQUEST: body deserialize error: {}", e.0); code = StatusCode::BAD_REQUEST; } else if let Some(e) = err.find::() { message = format!("BAD_REQUEST: body deserialize error: {}", e); code = StatusCode::BAD_REQUEST; } else if let Some(e) = err.find::() { code = StatusCode::BAD_REQUEST; message = format!("BAD_REQUEST: invalid query: {}", e); } else if let Some(e) = err.find::() { code = StatusCode::INTERNAL_SERVER_ERROR; message = format!("UNHANDLED_ERROR: {:?}", e.0); } else if let Some(e) = err.find::() { code = StatusCode::NOT_FOUND; message = format!("NOT_FOUND: {}", e.0); } else if let Some(e) = err.find::() { code = StatusCode::BAD_REQUEST; message = format!("BAD_REQUEST: {}", e.0); } else if let Some(e) = err.find::() { code = StatusCode::INTERNAL_SERVER_ERROR; message = format!("INTERNAL_SERVER_ERROR: {}", e.0); } else if let Some(e) = err.find::() { code = StatusCode::ACCEPTED; message = format!( "ACCEPTED: the object was broadcast to the network without being \ fully imported to the local database: {}", e.0 ); } else if let Some(e) = err.find::() { code = StatusCode::BAD_REQUEST; message = format!("BAD_REQUEST: Invalid object: {}", e.0); } else if let Some(e) = err.find::() { code = StatusCode::SERVICE_UNAVAILABLE; message = format!("SERVICE_UNAVAILABLE: beacon node is syncing: {}", e.0); } else if let Some(e) = err.find::() { code = StatusCode::FORBIDDEN; message = format!("FORBIDDEN: Invalid auth token: {}", e.0); } else if let Some(e) = err.find::() { if e.name().eq("Authorization") { code = StatusCode::UNAUTHORIZED; message = "UNAUTHORIZED: missing Authorization header".to_string(); } else { code = StatusCode::BAD_REQUEST; message = format!("BAD_REQUEST: missing {} header", e.name()); } } else if let Some(e) = err.find::() { code = StatusCode::BAD_REQUEST; message = format!("BAD_REQUEST: invalid {} header", e.name()); } else if err.find::().is_some() { code = StatusCode::METHOD_NOT_ALLOWED; message = "METHOD_NOT_ALLOWED".to_string(); } else { code = StatusCode::INTERNAL_SERVER_ERROR; message = "UNHANDLED_REJECTION".to_string(); } let json = warp::reply::json(&ErrorMessage { code: code.as_u16(), message, stacktraces: vec![], }); Ok(warp::reply::with_status(json, code)) } /// Convert a warp `Rejection` into a `Response`. /// /// This function should *always* be used to convert rejections into responses. This prevents warp /// from trying to backtrack in strange ways. See: https://github.com/sigp/lighthouse/issues/3404 pub async fn convert_rejection(res: Result) -> Response { match res { Ok(response) => response.into_response(), Err(e) => { let Ok(reply) = handle_rejection(e).await; reply.into_response() } } }