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
121
122
123
124
125
126
127
128
129
130
131
132
use http::status::StatusCode;
use mime::Mime;
use crate::http::{header, Response, Request};
use crate::http::errors::*;

pub trait Catcher: Send + Sync + 'static {
    fn catch(&self, req: &Request, resp: &mut Response)->bool;
}

fn error_html(e: &Box<dyn HttpError>)->String {
    format!("<!DOCTYPE html>
<html>
<head>
    <meta charset=\"utf-8\">
    <title>{0}: {1}</title>
    </head>
    <body align=\"center\">
        <div align=\"center\">
            <h1>{0}: {1}</h1>
            <h3>{2}</h3>
            <p>{3}</p>
            <hr />
            <small>salvo</small>
        </div>
    </body>
</html>", e.code(), e.name(), e.summary(), e.detail())
}
fn error_json(e: &Box<dyn HttpError>)->String {
    format!("{{\"error\":{{\"code\":{},\"name\":\"{}\",\"summary\":\"{}\",\"detail\":\"{}\"}}}}",
        e.code().as_u16(), e.name(), e.summary(), e.detail())
}
fn error_text(e: &Box<dyn HttpError>)->String {
   format!("code:{},\nname:{},\nsummary:{},\ndetail:{}", e.code(), e.name(), e.summary(), e.detail())
}
fn error_xml(e: &Box<dyn HttpError>)->String {
    format!("<error><code>{}</code><name>{}</name><summary>{}</summary><detail>{}</detail></error>", 
        e.code(), e.name(), e.summary(), e.detail())
}
pub struct CatcherImpl(Box<dyn HttpError>);
impl CatcherImpl{
    pub fn new(e: Box<dyn HttpError>) -> CatcherImpl{
        CatcherImpl(e)
    }
}
impl Catcher for CatcherImpl {
    fn catch(&self, req: &Request, resp: &mut Response)->bool {
        let status = resp.status_code().unwrap_or(StatusCode::NOT_FOUND);
        if status != self.0.code() {
            return false;
        }
        let dmime: Mime = "text/html".parse().unwrap();
        let accept = req.accept();
        let mut format = accept.first().unwrap_or(&dmime);
        if format.subtype() != mime::JSON && format.subtype() != mime::HTML {
            format = &dmime;
        }
        resp.headers_mut().insert(header::CONTENT_TYPE, format.to_string().parse().unwrap());
        let err = if resp.http_error.is_some() {
            resp.http_error.as_ref().unwrap()
        } else {
            &self.0
        };
        let content = match format.subtype().as_ref(){
            "text"=> error_text(err),
            "json"=> error_json(err),
            "xml"=> error_xml(err),
            _ => error_html(err),
        };
        resp.body_writers.push(Box::new(content));
        true
    }
}

macro_rules! default_catchers {
    ($($name:ty),+) => (
        let mut list: Vec<Box<dyn Catcher>> = vec![];
        $(
            list.push(Box::new(CatcherImpl::new(Box::new(<$name>::with_default()))));
        )+
        list
    )
}

pub mod defaults {
    use super::{Catcher, CatcherImpl};
    use crate::http::errors::http_error::*;

    pub fn get() -> Vec<Box<dyn Catcher>> {
        default_catchers! {
            BadRequestError,       
            UnauthorizedError,     
            PaymentRequiredError,  
            ForbiddenError,        
            NotFoundError,         
            MethodNotAllowedError, 
            NotAcceptableError,    
            ProxyAuthenticationRequiredError,
            RequestTimeoutError,       
            ConflictError,             
            GoneError,                 
            LengthRequiredError,       
            PreconditionFailedError,   
            PayloadTooLargeError,      
            UriTooLongError,           
            UnsupportedMediaTypeError, 
            RangeNotSatisfiableError,  
            ExpectationFailedError,    
            ImATeapotError,            
            MisdirectedRequestError,   
            UnprocessableEntityError,  
            LockedError,               
            FailedDependencyError,     
            UpgradeRequiredError,      
            PreconditionRequiredError, 
            TooManyRequestsError,      
            RequestHeaderFieldsTooLargeError,
            UnavailableForLegalReasonsError, 
            InternalServerError,    
            NotImplementedError,    
            BadGatewayError,        
            ServiceUnavailableError,
            GatewayTimeoutError,    
            HttpVersionNotSupportedError,
            VariantAlsoNegotiatesError,
            InsufficientStorageError,  
            LoopDetectedError,         
            NotExtendedError,          
            NetworkAuthenticationRequiredError
        }
    }
}