Coverage Report

Created: 2026-01-13 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}