monolake_services/common/
timeout.rs

1//! Timeout service for adding timeout functionality to HTTP handlers.
2//!
3//! This module provides a `TimeoutService` that wraps an inner service and applies
4//! a timeout to its execution. It's designed to work seamlessly with the `service_async`
5//! framework and can be easily integrated into a service stack.
6//!
7//! # Key Components
8//!
9//! - [`TimeoutService`]: The main service component that adds timeout functionality to an inner
10//!   service.
11//! - [`TimeoutError`]: Error type for timeout-related errors.
12//! - [`Timeout`]: A simple wrapper around `Duration` for configuration purposes.
13//!
14//! # Features
15//!
16//! - Adds configurable timeout to any inner service
17//! - Propagates inner service errors alongside timeout errors
18//!
19//! # Performance Considerations
20//!
21//! - Adds minimal overhead to the inner service execution
22//! - Uses efficient timeout mechanism provided by the `monoio` runtime
23
24use std::time::Duration;
25
26use monoio::time::timeout;
27use service_async::{
28    layer::{layer_fn, FactoryLayer},
29    AsyncMakeService, MakeService, Param, Service,
30};
31
32/// Service that adds timeout functionality to an inner service.
33#[derive(Clone)]
34pub struct TimeoutService<T> {
35    pub timeout: Duration,
36    pub inner: T,
37}
38
39#[derive(thiserror::Error, Debug)]
40pub enum TimeoutError<E> {
41    #[error("inner error: {0:?}")]
42    Inner(E),
43    #[error("timeout")]
44    Timeout,
45}
46
47impl<R, T: Service<R>> Service<R> for TimeoutService<T> {
48    type Response = T::Response;
49    type Error = TimeoutError<T::Error>;
50
51    async fn call(&self, req: R) -> Result<Self::Response, Self::Error> {
52        match timeout(self.timeout, self.inner.call(req)).await {
53            Ok(Ok(resp)) => Ok(resp),
54            Ok(Err(err)) => Err(TimeoutError::Inner(err)),
55            Err(_) => Err(TimeoutError::Timeout),
56        }
57    }
58}
59
60#[derive(Debug, Clone, Copy)]
61pub struct Timeout(pub Duration);
62
63impl<F> TimeoutService<F> {
64    pub fn layer<C>() -> impl FactoryLayer<C, F, Factory = Self>
65    where
66        C: Param<Timeout>,
67    {
68        layer_fn(|c: &C, inner| TimeoutService {
69            timeout: c.param().0,
70            inner,
71        })
72    }
73}
74
75impl<F: MakeService> MakeService for TimeoutService<F> {
76    type Service = TimeoutService<F::Service>;
77    type Error = F::Error;
78
79    fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
80        Ok(TimeoutService {
81            timeout: self.timeout,
82            inner: self
83                .inner
84                .make_via_ref(old.map(|o| &o.inner))
85                .map_err(Into::into)?,
86        })
87    }
88}
89
90impl<F: AsyncMakeService> AsyncMakeService for TimeoutService<F> {
91    type Service = TimeoutService<F::Service>;
92    type Error = F::Error;
93
94    async fn make_via_ref(
95        &self,
96        old: Option<&Self::Service>,
97    ) -> Result<Self::Service, Self::Error> {
98        Ok(TimeoutService {
99            timeout: self.timeout,
100            inner: self
101                .inner
102                .make_via_ref(old.map(|o| &o.inner))
103                .await
104                .map_err(Into::into)?,
105        })
106    }
107}