Skip to main content

xitca_web/error/
router.rs

1pub use xitca_http::util::service::{
2    route::MethodNotAllowed,
3    router::{MatchError, RouterError},
4};
5
6use core::convert::Infallible;
7
8use crate::{
9    WebContext,
10    body::ResponseBody,
11    http::{StatusCode, WebResponse, header::ALLOW},
12    service::Service,
13};
14
15use super::{Error, error_from_service};
16
17error_from_service!(MatchError);
18
19impl<'r, C, B> Service<WebContext<'r, C, B>> for MatchError {
20    type Response = WebResponse;
21    type Error = Infallible;
22
23    async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
24        #[cfg(feature = "grpc")]
25        if is_grpc_request(&ctx) {
26            return super::grpc::GrpcError::new(super::grpc::GrpcStatus::Unimplemented, "method not found")
27                .call(ctx)
28                .await;
29        }
30
31        let mut res = ctx.into_response(ResponseBody::empty());
32        *res.status_mut() = StatusCode::NOT_FOUND;
33        Ok(res)
34    }
35}
36
37error_from_service!(MethodNotAllowed);
38
39impl<'r, C, B> Service<WebContext<'r, C, B>> for MethodNotAllowed {
40    type Response = WebResponse;
41    type Error = Infallible;
42
43    async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
44        #[cfg(feature = "grpc")]
45        if is_grpc_request(&ctx) {
46            return super::grpc::GrpcError::new(super::grpc::GrpcStatus::Unimplemented, "method not allowed")
47                .call(ctx)
48                .await;
49        }
50
51        let mut res = ctx.into_response(ResponseBody::empty());
52
53        let allowed = self.allowed_methods();
54
55        let len = allowed.iter().fold(0, |a, m| a + m.as_str().len() + 1);
56
57        let mut methods = String::with_capacity(len);
58
59        for method in allowed {
60            methods.push_str(method.as_str());
61            methods.push(',');
62        }
63        methods.pop();
64
65        res.headers_mut().insert(ALLOW, methods.parse().unwrap());
66        *res.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
67
68        Ok(res)
69    }
70}
71
72#[cfg(feature = "grpc")]
73fn is_grpc_request<C, B>(ctx: &WebContext<'_, C, B>) -> bool {
74    use crate::http::{const_header_value::GRPC, header::CONTENT_TYPE};
75    ctx.req()
76        .headers()
77        .get(CONTENT_TYPE)
78        .is_some_and(|v| v.as_bytes().starts_with(GRPC.as_bytes()))
79}
80
81impl<E> From<RouterError<E>> for Error
82where
83    E: Into<Self>,
84{
85    fn from(e: RouterError<E>) -> Self {
86        match e {
87            RouterError::Match(e) => e.into(),
88            RouterError::NotAllowed(e) => e.into(),
89            RouterError::Service(e) => e.into(),
90        }
91    }
92}