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