/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-3.1.4/src/timings.rs
Line | Count | Source |
1 | | use std::fmt; |
2 | | use std::iter::once; |
3 | | use std::sync::Arc; |
4 | | |
5 | | use crate::config::Timeouts; |
6 | | use crate::transport::time::{Duration, Instant}; |
7 | | |
8 | | /// The various timeouts. |
9 | | /// |
10 | | /// Each enum corresponds to a value in |
11 | | /// [`ConfigBuilder::timeout_xxx`][crate::config::ConfigBuilder::timeout_global]. |
12 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
13 | | #[non_exhaustive] |
14 | | pub enum Timeout { |
15 | | /// Timeout for entire operation. |
16 | | Global, |
17 | | |
18 | | /// Timeout for the current call (when redirected). |
19 | | PerCall, |
20 | | |
21 | | /// Timeout in the resolver. |
22 | | Resolve, |
23 | | |
24 | | /// Timeout while opening the connection. |
25 | | Connect, |
26 | | |
27 | | /// Timeout while sending the request headers. |
28 | | SendRequest, |
29 | | |
30 | | /// Internal value never seen outside ureq (since awaiting 100 is expected |
31 | | /// to timeout). |
32 | | #[doc(hidden)] |
33 | | Await100, |
34 | | |
35 | | /// Timeout when sending then request body. |
36 | | SendBody, |
37 | | |
38 | | /// Timeout while receiving the response headers. |
39 | | RecvResponse, |
40 | | |
41 | | /// Timeout while receiving the response body. |
42 | | RecvBody, |
43 | | } |
44 | | |
45 | | impl Timeout { |
46 | | /// Give the immediate preceeding Timeout |
47 | 0 | fn preceeding(&self) -> impl Iterator<Item = Timeout> { |
48 | 0 | let prev: &[Timeout] = match self { |
49 | 0 | Timeout::Resolve => &[Timeout::PerCall], |
50 | 0 | Timeout::Connect => &[Timeout::Resolve], |
51 | 0 | Timeout::SendRequest => &[Timeout::Connect], |
52 | 0 | Timeout::Await100 => &[Timeout::SendRequest], |
53 | 0 | Timeout::SendBody => &[Timeout::SendRequest, Timeout::Await100], |
54 | 0 | Timeout::RecvResponse => &[Timeout::SendRequest, Timeout::SendBody], |
55 | 0 | Timeout::RecvBody => &[Timeout::RecvResponse], |
56 | 0 | _ => &[], |
57 | | }; |
58 | | |
59 | 0 | prev.iter().copied() |
60 | 0 | } |
61 | | |
62 | | /// All timeouts to check |
63 | 0 | fn timeouts_to_check(&self) -> impl Iterator<Item = Timeout> { |
64 | | // Always check Global and PerCall |
65 | 0 | once(*self) |
66 | 0 | .chain(self.preceeding()) |
67 | 0 | .chain([Timeout::Global, Timeout::PerCall]) |
68 | 0 | } |
69 | | |
70 | | /// Get the corresponding configured timeout |
71 | 0 | fn configured_timeout(&self, timeouts: &Timeouts) -> Option<Duration> { |
72 | 0 | match self { |
73 | 0 | Timeout::Global => timeouts.global, |
74 | 0 | Timeout::PerCall => timeouts.per_call, |
75 | 0 | Timeout::Resolve => timeouts.resolve, |
76 | 0 | Timeout::Connect => timeouts.connect, |
77 | 0 | Timeout::SendRequest => timeouts.send_request, |
78 | 0 | Timeout::Await100 => timeouts.await_100, |
79 | 0 | Timeout::SendBody => timeouts.send_body, |
80 | 0 | Timeout::RecvResponse => timeouts.recv_response, |
81 | 0 | Timeout::RecvBody => timeouts.recv_body, |
82 | | } |
83 | 0 | .map(Into::into) |
84 | 0 | } |
85 | | } |
86 | | |
87 | | #[derive(Default, Debug)] |
88 | | pub(crate) struct CallTimings { |
89 | | timeouts: Box<Timeouts>, |
90 | | current_time: CurrentTime, |
91 | | times: Vec<(Timeout, Instant)>, |
92 | | } |
93 | | |
94 | | impl CallTimings { |
95 | 0 | pub(crate) fn new(timeouts: Timeouts, current_time: CurrentTime) -> Self { |
96 | 0 | let mut times = Vec::with_capacity(8); |
97 | | |
98 | 0 | let now = current_time.now(); |
99 | 0 | times.push((Timeout::Global, now)); |
100 | 0 | times.push((Timeout::PerCall, now)); |
101 | | |
102 | 0 | CallTimings { |
103 | 0 | timeouts: Box::new(timeouts), |
104 | 0 | current_time, |
105 | 0 | times, |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | 0 | pub(crate) fn new_call(mut self) -> CallTimings { |
110 | 0 | self.times.truncate(1); // Global is in position 0. |
111 | 0 | self.times.push((Timeout::PerCall, self.current_time.now())); |
112 | | |
113 | 0 | CallTimings { |
114 | 0 | timeouts: self.timeouts, |
115 | 0 | current_time: self.current_time, |
116 | 0 | times: self.times, |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | 0 | pub(crate) fn now(&self) -> Instant { |
121 | 0 | self.current_time.now() |
122 | 0 | } |
123 | | |
124 | 0 | pub(crate) fn record_time(&mut self, timeout: Timeout) { |
125 | | // Each time should only be recorded once |
126 | 0 | assert!( |
127 | 0 | self.time_of(timeout).is_none(), |
128 | 0 | "{:?} recorded more than once", |
129 | | timeout |
130 | | ); |
131 | | |
132 | | // There need to be at least one preceeding time recorded |
133 | | // since it follows a graph/call tree. |
134 | 0 | let any_preceeding = timeout |
135 | 0 | .preceeding() |
136 | 0 | .filter_map(|to_check| self.time_of(to_check)) |
137 | 0 | .any(|_| true); |
138 | | |
139 | 0 | assert!(any_preceeding, "{:?} has no preceeding", timeout); |
140 | | |
141 | | // Record the time |
142 | 0 | self.times.push((timeout, self.current_time.now())); |
143 | 0 | } |
144 | | |
145 | 0 | fn time_of(&self, timeout: Timeout) -> Option<Instant> { |
146 | 0 | self.times.iter().find(|x| x.0 == timeout).map(|x| x.1) |
147 | 0 | } |
148 | | |
149 | 0 | pub(crate) fn next_timeout(&self, timeout: Timeout) -> NextTimeout { |
150 | 0 | let now = self.now(); |
151 | | |
152 | 0 | let (reason, at) = timeout |
153 | 0 | .timeouts_to_check() |
154 | 0 | .filter_map(|to_check| { |
155 | 0 | let time = if to_check == timeout { |
156 | 0 | now |
157 | | } else { |
158 | 0 | self.time_of(to_check)? |
159 | | }; |
160 | 0 | let timeout = to_check.configured_timeout(&self.timeouts)?; |
161 | 0 | Some((to_check, time + timeout)) |
162 | 0 | }) |
163 | 0 | .min_by(|a, b| a.1.cmp(&b.1)) |
164 | 0 | .unwrap_or((Timeout::Global, Instant::NotHappening)); |
165 | | |
166 | 0 | let after = at.duration_since(now); |
167 | | |
168 | 0 | NextTimeout { after, reason } |
169 | 0 | } |
170 | | } |
171 | | |
172 | | #[derive(Clone)] |
173 | | pub(crate) struct CurrentTime(Arc<dyn Fn() -> Instant + Send + Sync + 'static>); |
174 | | |
175 | | impl CurrentTime { |
176 | 0 | pub(crate) fn now(&self) -> Instant { |
177 | 0 | self.0() |
178 | 0 | } |
179 | | } |
180 | | |
181 | | /// A pair of [`Duration`] and [`Timeout`]. |
182 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
183 | | pub struct NextTimeout { |
184 | | /// Duration until next timeout. |
185 | | pub after: Duration, |
186 | | /// The name of the next timeout.s |
187 | | pub reason: Timeout, |
188 | | } |
189 | | |
190 | | impl NextTimeout { |
191 | | /// Returns the duration of the timeout if the timeout must happen, but avoid instant timeouts |
192 | | /// |
193 | | /// If the timeout must happen but is zero, returns 1 second |
194 | 0 | pub fn not_zero(&self) -> Option<Duration> { |
195 | 0 | if self.after.is_not_happening() { |
196 | 0 | None |
197 | 0 | } else if self.after.is_zero() { |
198 | 0 | Some(Duration::from_secs(1)) |
199 | | } else { |
200 | 0 | Some(self.after) |
201 | | } |
202 | 0 | } |
203 | | } |
204 | | |
205 | | impl fmt::Debug for CurrentTime { |
206 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
207 | 0 | f.debug_tuple("CurrentTime").finish() |
208 | 0 | } |
209 | | } |
210 | | |
211 | | impl Default for CurrentTime { |
212 | 0 | fn default() -> Self { |
213 | 0 | Self(Arc::new(Instant::now)) |
214 | 0 | } |
215 | | } |
216 | | |
217 | | impl fmt::Display for Timeout { |
218 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
219 | 0 | let r = match self { |
220 | 0 | Timeout::Global => "global", |
221 | 0 | Timeout::PerCall => "per call", |
222 | 0 | Timeout::Resolve => "resolve", |
223 | 0 | Timeout::Connect => "connect", |
224 | 0 | Timeout::SendRequest => "send request", |
225 | 0 | Timeout::SendBody => "send body", |
226 | 0 | Timeout::Await100 => "await 100", |
227 | 0 | Timeout::RecvResponse => "receive response", |
228 | 0 | Timeout::RecvBody => "receive body", |
229 | | }; |
230 | 0 | write!(f, "{}", r) |
231 | 0 | } |
232 | | } |