• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use std::future::Future;
15 use std::pin::Pin;
16 use std::task::{Context, Poll};
17 use std::time::Duration;
18 
19 use super::Response;
20 use crate::error::HttpClientError;
21 use crate::runtime::{sleep, Sleep};
22 
23 pub(crate) struct TimeoutFuture<T, F> {
24     pub(crate) timeout: Option<Pin<Box<Sleep>>>,
25     pub(crate) future: T,
26     pub(crate) set_timeout: Option<F>,
27 }
28 
29 impl<T, F> TimeoutFuture<Pin<Box<T>>, F>
30 where
31     F: FnOnce(&mut Response, Option<Pin<Box<Sleep>>>),
32 {
new(future: T, timeout: Duration, set_timeout: F) -> Self33     pub(crate) fn new(future: T, timeout: Duration, set_timeout: F) -> Self {
34         Self {
35             timeout: Some(Box::pin(sleep(timeout))),
36             future: Box::pin(future),
37             set_timeout: Some(set_timeout),
38         }
39     }
40 }
41 
42 impl<T, F> Future for TimeoutFuture<T, F>
43 where
44     T: Future<Output = Result<Response, HttpClientError>> + Unpin,
45     F: FnOnce(&mut Response, Option<Pin<Box<Sleep>>>) + Unpin,
46 {
47     type Output = Result<Response, HttpClientError>;
48 
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>49     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
50         let this = self.get_mut();
51 
52         if let Some(delay) = this.timeout.as_mut() {
53             if let Poll::Ready(()) = delay.as_mut().poll(cx) {
54                 return Poll::Ready(err_from_io!(Timeout, std::io::ErrorKind::TimedOut.into()));
55             }
56         }
57         match Pin::new(&mut this.future).poll(cx) {
58             Poll::Ready(Ok(mut response)) => {
59                 if let Some(set_timeout) = this.set_timeout.take() {
60                     set_timeout(&mut response, this.timeout.take());
61                 }
62                 Poll::Ready(Ok(response))
63             }
64             Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
65             Poll::Pending => Poll::Pending,
66         }
67     }
68 }
69 
70 #[cfg(all(test, feature = "ylong_base"))]
71 mod ut_timeout {
72     use std::pin::Pin;
73     use std::sync::Arc;
74 
75     use ylong_http::response::status::StatusCode;
76     use ylong_http::response::{Response, ResponsePart};
77     use ylong_http::version::Version;
78 
79     use crate::async_impl::timeout::TimeoutFuture;
80     use crate::async_impl::HttpBody;
81     use crate::runtime::Sleep;
82     use crate::util::interceptor::IdleInterceptor;
83     use crate::util::normalizer::BodyLength;
84     use crate::HttpClientError;
85 
86     /// UT test cases for `TimeoutFuture`.
87     ///
88     /// # Brief
89     /// 1. Creates a `Future`.
90     /// 2. Calls `ylong_runtime::block_on` to run the future.
91     /// 3. Checks if result is correct.
92     #[test]
ut_timeout_future()93     fn ut_timeout_future() {
94         let future1 = Box::pin(async {
95             let part = ResponsePart {
96                 version: Version::HTTP1_1,
97                 status: StatusCode::OK,
98                 headers: Default::default(),
99             };
100             let body = HttpBody::new(
101                 Arc::new(IdleInterceptor),
102                 BodyLength::Empty,
103                 Box::new([].as_slice()),
104                 &[],
105             )
106             .unwrap();
107             Ok(crate::async_impl::Response::new(Response::from_raw_parts(
108                 part, body,
109             )))
110         });
111 
112         let future2 = Box::pin(async {
113             Result::<crate::async_impl::Response, HttpClientError>::Err(
114                 HttpClientError::user_aborted(),
115             )
116         });
117 
118         let time_future1 = TimeoutFuture {
119             timeout: None,
120             future: future1,
121             set_timeout: Some(
122                 |response: &mut super::Response, timeout: Option<Pin<Box<Sleep>>>| {
123                     response.body_mut().set_total_sleep(timeout);
124                 },
125             ),
126         };
127 
128         let time_future2 = TimeoutFuture {
129             timeout: None,
130             future: future2,
131             set_timeout: Some(
132                 |response: &mut super::Response, timeout: Option<Pin<Box<Sleep>>>| {
133                     response.body_mut().set_request_sleep(timeout);
134                 },
135             ),
136         };
137 
138         assert!(ylong_runtime::block_on(time_future1).is_ok());
139         assert!(ylong_runtime::block_on(time_future2).is_err());
140     }
141 }
142