Skip to main content

gateway_runtime/layers/
error.rs

1//! # Error Handling Layer
2//!
3//! This layer intercepts errors returned by the inner service and converts them into
4//! valid HTTP responses using a configured [ErrorHandler].
5//!
6//! This is crucial for returning user-friendly error messages (e.g., JSON) instead of
7//! raw server errors or dropped connections.
8
9use crate::alloc::boxed::Box;
10use crate::gateway::ErrorHandler;
11use crate::{GatewayError, GatewayRequest, GatewayResponse};
12use core::task::{Context, Poll};
13use std::future::Future;
14use std::pin::Pin;
15use tower::Service;
16
17/// A Tower middleware that handles errors from the inner service.
18#[derive(Clone)]
19pub struct ErrorLayer<S> {
20    inner: S,
21    handler: Option<ErrorHandler>,
22}
23
24impl<S> ErrorLayer<S> {
25    /// Creates a new `ErrorLayer`.
26    ///
27    /// # Parameters
28    /// *   `inner`: The inner service to wrap.
29    /// *   `handler`: An optional custom error handler. If `None`, errors are propagated.
30    pub fn new(inner: S, handler: Option<ErrorHandler>) -> Self {
31        Self { inner, handler }
32    }
33}
34
35impl<S> Service<GatewayRequest> for ErrorLayer<S>
36where
37    S: Service<GatewayRequest, Response = GatewayResponse, Error = GatewayError>,
38    S::Future: Send + 'static,
39{
40    type Response = GatewayResponse;
41    type Error = GatewayError;
42    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
43
44    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
45        self.inner.poll_ready(cx)
46    }
47
48    fn call(&mut self, req: GatewayRequest) -> Self::Future {
49        // Capture minimal request context (Method, URI, Headers) to pass to the error handler.
50        // The body is not cloned to avoid performance penalties.
51        let method = req.method().clone();
52        let uri = req.uri().clone();
53        let headers = req.headers().clone();
54
55        let handler_clone = self.handler.clone();
56
57        let fut = self.inner.call(req);
58
59        Box::pin(async move {
60            match fut.await {
61                Ok(resp) => Ok(resp),
62                Err(err) => {
63                    if let Some(h) = handler_clone {
64                        // Reconstruct a partial request for the handler context.
65                        // The body is empty since the original request has been consumed.
66                        let mut partial_req = http::Request::builder()
67                            .method(method)
68                            .uri(uri)
69                            .body(crate::alloc::vec::Vec::new())
70                            .unwrap();
71                        *partial_req.headers_mut() = headers;
72
73                        // Execute the custom error handler
74                        Ok(h(&partial_req, err))
75                    } else {
76                        // Propagate the error if no handler is configured
77                        Err(err)
78                    }
79                }
80            }
81        })
82    }
83}