Skip to main content

xitca_web/error/
mod.rs

1//! web error types.
2//!
3//! In xitca-web error is treated as high level type and handled lazily.
4//!
5//! - high level:
6//!   An error type is represented firstly and mostly as a Rust type with useful trait bounds.It doesn't
7//!   necessarily mapped and/or converted into http response immediately. User is encouraged to pass the
8//!   error value around and convert it to http response on condition they prefer.
9//!
10//! - lazy:
11//!   Since an error is passed as value mostly the error is handled lazily when the value is needed.
12//!   Including but not limiting to: formatting, logging, generating http response.
13//!
14//! # Example
15//! ```rust
16//! # use xitca_web::{
17//! #   error::Error,
18//! #   handler::{handler_service, html::Html, Responder},
19//! #   http::{StatusCode, WebResponse},
20//! #   service::Service,
21//! #   App, WebContext};
22//! // a handler function always produce error.
23//! async fn handler() -> Error {
24//!     Error::from(StatusCode::BAD_REQUEST)
25//! }
26//!
27//! // construct application with handler function and middleware.
28//! App::new()
29//!     .at("/", handler_service(handler))
30//!     .enclosed_fn(error_handler);
31//!
32//! // a handler middleware observe route services output.
33//! async fn error_handler<S>(service: &S, mut ctx: WebContext<'_>) -> Result<WebResponse, Error>
34//! where
35//!     S: for<'r> Service<WebContext<'r>, Response = WebResponse, Error = Error>
36//! {
37//!     // unlike WebResponse which is already a valid http response. the error is treated as it's
38//!     // onw type on the other branch of the Result enum.  
39//!
40//!     // since the handler function at the start of example always produce error. our middleware
41//!     // will always observe the Error type value so let's unwrap it.
42//!     let err = service.call(ctx.reborrow()).await.err().unwrap();
43//!     
44//!     // now we have the error value we can start to interact with it and add our logic of
45//!     // handling it.
46//!
47//!     // we can print the error.
48//!     println!("{err}");
49//!
50//!     // we can log the error.
51//!     tracing::error!("{err}");
52//!
53//!     // we can render the error to html and convert it to http response.
54//!     let html = format!("<!DOCTYPE html>\
55//!         <html>\
56//!         <body>\
57//!         <h1>{err}</h1>\
58//!         </body>\
59//!         </html>");
60//!     return (Html(html), StatusCode::BAD_REQUEST).respond(ctx).await;
61//!
62//!     // or by default the error value is returned in Result::Err and passed to parent services
63//!     // of App or other middlewares where eventually it would be converted to WebResponse.
64//!     
65//!     // "eventually" can either mean a downstream user provided error handler middleware/service
66//!     // or the implicit catch all error middleware xitca-web offers. In the latter case a default
67//!     // WebResponse is generated with minimal information describing the reason of error.
68//!
69//!     Err(err)
70//! }
71//! ```
72
73mod body;
74mod extension;
75#[cfg(feature = "grpc")]
76mod grpc;
77mod header;
78mod router;
79mod status;
80
81pub use body::*;
82pub use extension::*;
83#[cfg(feature = "grpc")]
84pub use grpc::*;
85pub use header::*;
86pub use router::*;
87pub use status::*;
88
89use core::{any::Any, convert::Infallible, fmt};
90
91use std::{error, io, sync::Mutex};
92
93use crate::{
94    context::WebContext,
95    http::WebResponse,
96    service::{Service, pipeline::PipelineE},
97};
98
99use self::service_impl::ErrorService;
100
101/// type erased error object. can be used for dynamic access to error's debug/display info.
102/// it also support upcasting and downcasting.
103///
104/// # Examples:
105/// ```rust
106/// use std::{convert::Infallible, error, fmt};
107///
108/// use xitca_web::{error::Error, http::WebResponse, service::Service, WebContext};
109///
110/// // concrete error type
111/// #[derive(Debug)]
112/// struct Foo;
113///
114/// // implement debug and display format.
115/// impl fmt::Display for Foo {
116///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117///         f.write_str("Foo")
118///     }
119/// }
120///
121/// // implement Error trait
122/// impl error::Error for Foo {}
123///
124/// // implement Service trait for http response generating.
125/// impl<'r, C> Service<WebContext<'r, C>> for Foo {
126///     type Response = WebResponse;
127///     type Error = Infallible;
128///
129///     async fn call(&self, _: WebContext<'r, C>) -> Result<Self::Response, Self::Error> {
130///         Ok(WebResponse::default())
131///     }
132/// }
133///
134/// async fn handle_error<C>(ctx: WebContext<'_, C>) where C: 'static {
135///     // construct error object.
136///     let e = Error::from_service(Foo);
137///
138///     // format and display error
139///     println!("{e:?}");
140///     println!("{e}");
141///
142///     // generate http response.
143///     let res = Service::call(&e, ctx).await.unwrap();
144///     assert_eq!(res.status().as_u16(), 200);
145///
146///     // upcast error to trait object of std::error::Error
147///     let e = e.upcast();
148///     
149///     // downcast error object to concrete type again
150///     assert!(e.downcast_ref::<Foo>().is_some());
151/// }
152/// ```
153pub struct Error(Box<dyn for<'r> ErrorService<WebContext<'r, Request<'r>>>>);
154
155// TODO: this is a temporary type to mirror std::error::request_ref API and latter should
156// be used when it's stabled.
157/// container for dynamic type provided by Error's default Service impl
158pub struct Request<'a> {
159    inner: &'a dyn Any,
160}
161
162impl Request<'_> {
163    /// request a reference of concrete type from dynamic container.
164    /// [Error] would provide your application's global state to it.
165    ///
166    /// # Examples
167    /// ```rust
168    /// use std::{convert::Infallible, fmt};
169    ///
170    /// use xitca_web::{
171    ///     error::{Error, Request},
172    ///     handler::handler_service,
173    ///     http::WebResponse,
174    ///     service::Service,
175    ///     App, WebContext
176    /// };
177    ///
178    /// let app = App::new()
179    ///     .at("/", handler_service(handler))
180    ///     .with_state(String::from("996")); // application has a root state of String type.
181    ///
182    /// // handler function returns custom error type
183    /// async fn handler(_: &WebContext<'_, String>) -> Result<&'static str, MyError> {
184    ///     Err(MyError)
185    /// }
186    ///
187    /// // a self defined error type and necessary error implements.
188    /// #[derive(Debug)]
189    /// struct MyError;
190    ///
191    /// impl fmt::Display for MyError {
192    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193    ///         f.write_str("my error")
194    ///     }
195    /// }
196    ///
197    /// impl std::error::Error for MyError {}
198    ///
199    /// impl From<MyError> for Error {
200    ///     fn from(e: MyError) -> Self {
201    ///         Self::from_service(e)
202    ///     }
203    /// }
204    ///
205    /// // Assuming application state is needed in error handler then this is the Service impl
206    /// // you want to write
207    /// impl<'r> Service<WebContext<'r, Request<'r>>> for MyError {
208    ///     type Response = WebResponse;
209    ///     type Error = Infallible;
210    ///
211    ///     async fn call(&self, ctx: WebContext<'r, Request<'r>>) -> Result<Self::Response, Self::Error> {
212    ///         // error::Request is able to provide application's state reference with runtime type casting.
213    ///         if let Some(state) = ctx.state().request_ref::<String>() {
214    ///             assert_eq!(state, "996");
215    ///         }
216    ///         todo!()
217    ///     }
218    /// }
219    /// ```
220    pub fn request_ref<C>(&self) -> Option<&C>
221    where
222        C: 'static,
223    {
224        self.inner.downcast_ref()
225    }
226}
227
228impl Error {
229    // construct an error object from given service type.
230    pub fn from_service<S>(s: S) -> Self
231    where
232        S: for<'r> Service<WebContext<'r, Request<'r>>, Response = WebResponse, Error = Infallible>
233            + error::Error
234            + Send
235            + Sync
236            + 'static,
237    {
238        Self(Box::new(s))
239    }
240
241    /// upcast Error to trait object for advanced error handling.
242    /// See [std::error::Error] for usage
243    pub fn upcast(&self) -> &(dyn error::Error + 'static) {
244        let e = self.0.dyn_err();
245        // due to Rust's specialization limitation Box<dyn std::error::Error> can impl neither
246        // std::error::Error nor service_impl::DynError traits. Therefore a StdError new type
247        // wrapper is introduced to work around it. When upcasting the error this new type is manually
248        // removed so the trait object have correct std::error::Error trait impl for the inner
249        // type
250        if let Some(e) = e.downcast_ref::<StdError>() {
251            return &*e.0;
252        }
253        e
254    }
255
256    /// Specialized error to response conversion to workaround trait impl specialization.
257    /// This API is often used when calling `Service::call(&Error)` from another error type's Service impl.
258    ///
259    /// # Examples
260    /// ```rust
261    /// use std::{convert::Infallible, fmt};
262    ///
263    /// use xitca_web::{
264    ///     error::{Error, Request},
265    ///     handler::{json::Json, Responder},
266    ///     http::WebResponse,
267    ///     service::Service,
268    ///     WebContext
269    /// };
270    ///
271    /// // a self defined error type and necessary error implements.
272    /// #[derive(Debug)]
273    /// struct MyError;
274    ///
275    /// impl fmt::Display for MyError {
276    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277    ///         f.write_str("my error")
278    ///     }
279    /// }
280    ///
281    /// impl std::error::Error for MyError {}
282    ///
283    /// impl From<MyError> for Error {
284    ///     fn from(e: MyError) -> Self {
285    ///         Self::from_service(e)
286    ///     }
287    /// }
288    ///
289    /// // Assuming application state is needed in error handler then this is the Service impl
290    /// // you want to write
291    /// impl<'r> Service<WebContext<'r, Request<'r>>> for MyError {
292    ///     type Response = WebResponse;
293    ///     type Error = Infallible;
294    ///
295    ///     async fn call(&self, mut ctx: WebContext<'r, Request<'r>>) -> Result<Self::Response, Self::Error> {
296    ///         // an invalid json object would fail to be conerted to web response.
297    ///         match Json(r#"{"key": value}"#).respond(ctx.reborrow()).await {
298    ///             Ok(res) => Ok(res),
299    ///             // assuming we want to convert the json failure as final error response to client
300    ///             // but <Error as Service>::call can't be used here due to it's trait impl bound.
301    ///             // instead we use Error::call_dyn instead to workaround the limitation
302    ///             Err(e) => e.call_dyn(ctx).await
303    ///         }
304    ///     }
305    /// }
306    /// ```
307    pub async fn call_dyn(&self, ctx: WebContext<'_, Request<'_>>) -> Result<WebResponse, Infallible> {
308        crate::service::object::ServiceObject::call(&self.0, ctx).await
309    }
310}
311
312impl fmt::Debug for Error {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        fmt::Debug::fmt(&*self.0, f)
315    }
316}
317
318impl fmt::Display for Error {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        fmt::Display::fmt(&*self.0, f)
321    }
322}
323
324impl error::Error for Error {
325    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
326        self.0.source()
327    }
328
329    #[cfg(feature = "nightly")]
330    fn provide<'a>(&'a self, request: &mut error::Request<'a>) {
331        self.0.provide(request)
332    }
333}
334
335impl<'r, C> Service<WebContext<'r, C>> for Error
336where
337    C: 'static,
338{
339    type Response = WebResponse;
340    type Error = Infallible;
341
342    async fn call(&self, ctx: WebContext<'r, C>) -> Result<Self::Response, Self::Error> {
343        let WebContext { req, body, ctx } = ctx;
344        self.call_dyn(WebContext {
345            req,
346            body,
347            ctx: &Request { inner: ctx as _ },
348        })
349        .await
350    }
351}
352
353macro_rules! error_from_service {
354    ($tt: ty) => {
355        impl From<$tt> for crate::error::Error {
356            fn from(e: $tt) -> Self {
357                Self::from_service(e)
358            }
359        }
360    };
361}
362
363pub(crate) use error_from_service;
364
365#[allow(unused_macros)]
366macro_rules! blank_error_service {
367    ($type: ty, $status: path) => {
368        impl<'r, C, B> crate::service::Service<crate::WebContext<'r, C, B>> for $type {
369            type Response = crate::http::WebResponse;
370            type Error = ::core::convert::Infallible;
371
372            async fn call(&self, ctx: crate::WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
373                let mut res = ctx.into_response(crate::body::ResponseBody::empty());
374                *res.status_mut() = $status;
375                Ok(res)
376            }
377        }
378    };
379}
380
381#[allow(unused_imports)]
382pub(crate) use blank_error_service;
383
384macro_rules! forward_blank_internal {
385    ($type: ty) => {
386        impl<'r, C, B> crate::service::Service<crate::WebContext<'r, C, B>> for $type {
387            type Response = crate::http::WebResponse;
388            type Error = ::core::convert::Infallible;
389
390            async fn call(&self, ctx: crate::WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
391                crate::http::StatusCode::INTERNAL_SERVER_ERROR.call(ctx).await
392            }
393        }
394    };
395}
396
397pub(crate) use forward_blank_internal;
398
399macro_rules! forward_blank_bad_request {
400    ($type: ty) => {
401        impl<'r, C, B> crate::service::Service<crate::WebContext<'r, C, B>> for $type {
402            type Response = crate::http::WebResponse;
403            type Error = ::core::convert::Infallible;
404
405            async fn call(&self, ctx: crate::WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
406                crate::http::StatusCode::BAD_REQUEST.call(ctx).await
407            }
408        }
409    };
410}
411
412pub(crate) use forward_blank_bad_request;
413
414impl From<Infallible> for Error {
415    fn from(e: Infallible) -> Self {
416        match e {}
417    }
418}
419
420impl<'r, C, B> Service<WebContext<'r, C, B>> for Infallible {
421    type Response = WebResponse;
422    type Error = Infallible;
423
424    async fn call(&self, _: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
425        unreachable!()
426    }
427}
428
429error_from_service!(io::Error);
430forward_blank_internal!(io::Error);
431
432type StdErr = Box<dyn error::Error + Send + Sync>;
433
434impl From<StdErr> for Error {
435    fn from(e: StdErr) -> Self {
436        // this is a hack for middleware::Limit where it wraps around request stream body
437        // and produce BodyOverFlow error and return it as BodyError. In the mean time
438        // BodyError is another type alias share the same real type of StdErr and both share
439        // the same conversion path when converting into Error.
440        //
441        // currently the downcast and clone is to restore BodyOverFlow's original Service impl
442        // where it will produce 400 bad request http response while StdErr will be producing
443        // 500 internal server error http response. As well as restoring downstream Error
444        // consumer's chance to downcast BodyOverFlow type.
445        //
446        // TODO: BodyError type should be replaced with Error in streaming interface.
447        if let Some(e) = e.downcast_ref::<BodyOverFlow>() {
448            return Self::from(e.clone());
449        }
450
451        Self(Box::new(StdError(e)))
452    }
453}
454
455forward_blank_internal!(StdErr);
456
457/*
458    new type for `Box<dyn std::error::Error + Send + Sync>`. produce minimal
459    "500 InternalServerError" response and forward formatting, error handling
460    to inner type.
461    In other words it's an error type keep it's original formatting and error
462    handling methods without a specific `Service` impl for generating custom
463    http response.
464*/
465struct StdError(StdErr);
466
467impl fmt::Debug for StdError {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        fmt::Debug::fmt(&self.0, f)
470    }
471}
472
473impl fmt::Display for StdError {
474    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475        fmt::Display::fmt(&self.0, f)
476    }
477}
478
479impl error::Error for StdError {
480    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
481        self.0.source()
482    }
483
484    #[cfg(feature = "nightly")]
485    fn provide<'a>(&'a self, request: &mut error::Request<'a>) {
486        self.0.provide(request);
487    }
488}
489
490error_from_service!(StdError);
491
492impl<'r, C, B> Service<WebContext<'r, C, B>> for StdError {
493    type Response = WebResponse;
494    type Error = Infallible;
495
496    async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
497        self.0.call(ctx).await
498    }
499}
500
501/// error happens when joining a thread. typically caused by code panic inside thread.
502/// [`CatchUnwind`] middleware is able to produce this error type.
503///
504/// # Examples:
505/// ```rust
506/// # use xitca_web::error::ThreadJoinError;
507/// fn handle_error(e: &ThreadJoinError) {
508///     // debug and display format thread join error. can only provide basic error message if the error
509///     // source is typical string.(for example generated by panic! macro or unwrap/expect methods)
510///     println!("{e:?}");
511///     println!("{e}");
512///
513///     // for arbitrary thread join error manual type downcast is needed.(for example generated by std::panic::panic_any)
514///     // the mutex lock inside is to satisfy xitca-web's error type's thread safe guarantee: Send and Sync auto traits.
515///     //
516///     // rust's std library only offers Send bound for thread join error and the mutex is solely for the purpose of making
517///     // the error bound to Send + Sync.
518///     let any = e.0.lock().unwrap();
519///
520///     // an arbitrary type we assume possibly being used as panic message.
521///     struct Foo;
522///
523///     if let Some(_foo) = any.downcast_ref::<Foo>() {
524///         // if downcast is succeed it's possible to handle the typed panic message.
525///     }
526///
527///     // otherwise there is basically no way to retrieve any meaningful information and it's best to just ignore the error.
528///     // xitca-web is able to generate minimal http response from it anyway.
529/// }
530/// ```
531///
532/// [`CatchUnwind`]: crate::middleware::CatchUnwind
533pub struct ThreadJoinError(pub Mutex<Box<dyn Any + Send>>);
534
535impl fmt::Debug for ThreadJoinError {
536    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537        f.debug_struct("ThreadJoinError").finish()
538    }
539}
540
541impl fmt::Display for ThreadJoinError {
542    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543        let any = self.0.lock().unwrap();
544
545        // only try to catch typical panic message. currently the cases covered are
546        // format string and string reference generated by panic! macro.
547        any.downcast_ref::<String>()
548            .map(String::as_str)
549            .or_else(|| any.downcast_ref::<&str>().copied())
550            .map(|msg| write!(f, "error joining thread: {msg}"))
551            // arbitrary panic message type has to be handled by user manually.
552            .unwrap_or_else(|| f.write_str("error joining thread: unknown. please consider downcast ThreadJoinError.0"))
553    }
554}
555
556impl error::Error for ThreadJoinError {}
557
558impl ThreadJoinError {
559    pub(crate) fn new(e: Box<dyn Any + Send>) -> Self {
560        Self(Mutex::new(e))
561    }
562}
563
564error_from_service!(ThreadJoinError);
565forward_blank_internal!(ThreadJoinError);
566
567impl<F, S> From<PipelineE<F, S>> for Error
568where
569    F: Into<Error>,
570    S: Into<Error>,
571{
572    fn from(pipe: PipelineE<F, S>) -> Self {
573        match pipe {
574            PipelineE::First(f) => f.into(),
575            PipelineE::Second(s) => s.into(),
576        }
577    }
578}
579
580mod service_impl {
581    use crate::service::object::ServiceObject;
582
583    use super::*;
584
585    /// helper trait for constraint error object to multiple bounds
586    pub trait ErrorService<Req>:
587        ServiceObject<Req, Response = WebResponse, Error = Infallible> + DynError + Send + Sync
588    {
589    }
590
591    impl<S, Req> ErrorService<Req> for S where
592        S: ServiceObject<Req, Response = WebResponse, Error = Infallible> + DynError + Send + Sync
593    {
594    }
595
596    /// helper trait for enabling error trait upcast without depending on nightly rust feature
597    /// (written when project MSRV is 1.79)
598    pub trait DynError: error::Error {
599        fn dyn_err(&self) -> &(dyn error::Error + 'static);
600    }
601
602    impl<E> DynError for E
603    where
604        E: error::Error + 'static,
605    {
606        fn dyn_err(&self) -> &(dyn error::Error + 'static) {
607            self
608        }
609    }
610}
611
612#[cfg(test)]
613mod test {
614    use xitca_unsafe_collection::futures::NowOrPanic;
615
616    use crate::{body::ResponseBody, http::StatusCode};
617
618    use super::*;
619
620    #[test]
621    fn cast() {
622        #[derive(Debug)]
623        struct Foo;
624
625        impl fmt::Display for Foo {
626            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
627                f.write_str("Foo")
628            }
629        }
630
631        impl error::Error for Foo {}
632
633        impl<'r, C> Service<WebContext<'r, C>> for Foo {
634            type Response = WebResponse;
635            type Error = Infallible;
636
637            async fn call(&self, _: WebContext<'r, C>) -> Result<Self::Response, Self::Error> {
638                Ok(WebResponse::new(ResponseBody::empty()))
639            }
640        }
641
642        let foo = Error::from_service(Foo);
643
644        println!("{foo:?}");
645        println!("{foo}");
646
647        let mut ctx = WebContext::new_test(());
648        let res = Service::call(&foo, ctx.as_web_ctx()).now_or_panic().unwrap();
649        assert_eq!(res.status().as_u16(), 200);
650
651        let err = Error::from(Box::new(Foo) as Box<dyn std::error::Error + Send + Sync>);
652
653        println!("{err:?}");
654        println!("{err}");
655
656        assert!(err.upcast().downcast_ref::<Foo>().is_some());
657
658        #[derive(Debug)]
659        struct Bar;
660
661        impl fmt::Display for Bar {
662            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
663                f.write_str("Foo")
664            }
665        }
666
667        impl error::Error for Bar {}
668
669        impl<'r> Service<WebContext<'r, Request<'r>>> for Bar {
670            type Response = WebResponse;
671            type Error = Infallible;
672
673            async fn call(&self, ctx: WebContext<'r, Request<'r>>) -> Result<Self::Response, Self::Error> {
674                let status = ctx.state().request_ref::<StatusCode>().unwrap();
675                Ok(WebResponse::builder().status(*status).body(Default::default()).unwrap())
676            }
677        }
678
679        let bar = Error::from_service(Bar);
680
681        let res = bar
682            .call(WebContext::new_test(StatusCode::IM_USED).as_web_ctx())
683            .now_or_panic()
684            .unwrap();
685
686        assert_eq!(res.status(), StatusCode::IM_USED);
687    }
688}