xitca_web/error/
router.rs1pub 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}