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