volo_http/client/layer/
timeout.rs1use motore::{layer::Layer, service::Service};
2use volo::context::Context;
3
4use crate::{
5 context::client::Config,
6 error::ClientError,
7 request::{Request, RequestPartsExt},
8};
9
10#[derive(Clone, Debug, Default)]
19pub struct Timeout;
20
21impl<S> Layer<S> for Timeout {
22 type Service = TimeoutService<S>;
23
24 fn layer(self, inner: S) -> Self::Service {
25 TimeoutService { inner }
26 }
27}
28
29pub struct TimeoutService<S> {
33 inner: S,
34}
35
36impl<Cx, B, S> Service<Cx, Request<B>> for TimeoutService<S>
37where
38 Cx: Context<Config = Config> + Send,
39 B: Send,
40 S: Service<Cx, Request<B>, Error = ClientError> + Send + Sync,
41{
42 type Response = S::Response;
43 type Error = S::Error;
44
45 async fn call(&self, cx: &mut Cx, req: Request<B>) -> Result<Self::Response, Self::Error> {
46 let timeout = cx.rpc_info().config().timeout().cloned();
47
48 if let Some(duration) = timeout {
49 let url = req.url();
50 let fut = self.inner.call(cx, req);
51 let sleep = tokio::time::sleep(duration);
52
53 tokio::select! {
54 res = fut => res,
55 _ = sleep => {
56 if let Some(url) = url {
57 tracing::warn!("[Volo-HTTP] request timeout on `{url}`");
58 }
59 Err(crate::error::client::timeout().with_endpoint(cx.rpc_info().callee()))
60 }
61 }
62 } else {
63 self.inner.call(cx, req).await
64 }
65 }
66}