matrix_sdk/config/
request.rs

1// Copyright 2021 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    fmt::{self, Debug},
17    num::NonZeroUsize,
18    time::Duration,
19};
20
21use matrix_sdk_common::debug::DebugStructExt;
22
23use crate::http_client::DEFAULT_REQUEST_TIMEOUT;
24
25/// Configuration for requests the `Client` makes.
26///
27/// This sets how often and for how long a request should be repeated. As well
28/// as how long a successful request is allowed to take.
29///
30/// By default requests are retried indefinitely and use no timeout.
31///
32/// # Examples
33///
34/// ```
35/// use matrix_sdk::config::RequestConfig;
36/// use std::time::Duration;
37///
38/// // This sets makes requests fail after a single send request and sets the timeout to 30s
39/// let request_config = RequestConfig::new()
40///     .disable_retry()
41///     .timeout(Duration::from_secs(30));
42/// ```
43#[derive(Copy, Clone)]
44pub struct RequestConfig {
45    pub(crate) timeout: Option<Duration>,
46    pub(crate) read_timeout: Option<Duration>,
47    pub(crate) retry_limit: Option<usize>,
48    pub(crate) max_retry_time: Option<Duration>,
49    pub(crate) max_concurrent_requests: Option<NonZeroUsize>,
50    pub(crate) force_auth: bool,
51}
52
53#[cfg(not(tarpaulin_include))]
54impl Debug for RequestConfig {
55    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
56        let Self {
57            timeout,
58            read_timeout,
59            retry_limit,
60            max_retry_time: retry_timeout,
61            force_auth,
62            max_concurrent_requests,
63        } = self;
64
65        let mut res = fmt.debug_struct("RequestConfig");
66        res.field("timeout", timeout)
67            .maybe_field("read_timeout", read_timeout)
68            .maybe_field("retry_limit", retry_limit)
69            .maybe_field("max_retry_time", retry_timeout)
70            .maybe_field("max_concurrent_requests", max_concurrent_requests);
71
72        if *force_auth {
73            res.field("force_auth", &true);
74        }
75
76        res.finish()
77    }
78}
79
80impl Default for RequestConfig {
81    fn default() -> Self {
82        Self {
83            timeout: Some(DEFAULT_REQUEST_TIMEOUT),
84            read_timeout: None,
85            retry_limit: Default::default(),
86            max_retry_time: Default::default(),
87            max_concurrent_requests: Default::default(),
88            force_auth: false,
89        }
90    }
91}
92
93impl RequestConfig {
94    /// Create a new default `RequestConfig`.
95    #[must_use]
96    pub fn new() -> Self {
97        Default::default()
98    }
99
100    /// Create a new `RequestConfig` with default values, except the retry limit
101    /// which is set to 3.
102    #[must_use]
103    pub fn short_retry() -> Self {
104        Self::default().retry_limit(3)
105    }
106
107    /// This is a convince method to disable the retries of a request. Setting
108    /// the `retry_limit` to `0` has the same effect.
109    #[must_use]
110    pub fn disable_retry(mut self) -> Self {
111        self.retry_limit = Some(0);
112        self
113    }
114
115    /// The number of times a request should be retried. The default is no
116    /// limit.
117    #[must_use]
118    pub fn retry_limit(mut self, retry_limit: usize) -> Self {
119        self.retry_limit = Some(retry_limit);
120        self
121    }
122
123    /// The total limit of request that are pending or run concurrently.
124    /// Any additional request beyond that number will be waiting until another
125    /// concurrent requests finished. Requests are queued fairly.
126    #[must_use]
127    pub fn max_concurrent_requests(mut self, limit: Option<NonZeroUsize>) -> Self {
128        self.max_concurrent_requests = limit;
129        self
130    }
131
132    /// Set the timeout duration for all HTTP requests.
133    #[must_use]
134    pub fn timeout(mut self, timeout: impl Into<Option<Duration>>) -> Self {
135        self.timeout = timeout.into();
136        self
137    }
138
139    /// Set the read timeout duration for all HTTP requests.
140    ///
141    /// The timeout applies to each read operation, and resets after a
142    /// successful read. This is more appropriate for detecting stalled
143    /// connections when the size isn’t known beforehand.
144    ///
145    /// **IMPORTANT**: note this value can only be applied when the HTTP client
146    /// is instantiated, it won't have any effect on a per-request basis.
147    #[must_use]
148    pub fn read_timeout(mut self, timeout: impl Into<Option<Duration>>) -> Self {
149        self.read_timeout = timeout.into();
150        self
151    }
152
153    /// Set a time limit for how long a request should be retried. The default
154    /// is that there isn't a limit, meaning requests are retried forever.
155    ///
156    /// This is a time-based variant of the [`RequestConfig::retry_limit`]
157    /// method.
158    #[must_use]
159    pub fn max_retry_time(mut self, retry_timeout: Duration) -> Self {
160        self.max_retry_time = Some(retry_timeout);
161        self
162    }
163
164    /// Force sending authorization even if the endpoint does not require it.
165    /// Default is only sending authorization if it is required.
166    #[must_use]
167    pub fn force_auth(mut self) -> Self {
168        self.force_auth = true;
169        self
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use std::time::Duration;
176
177    use super::RequestConfig;
178
179    #[test]
180    fn smoketest() {
181        let cfg = RequestConfig::new()
182            .force_auth()
183            .max_retry_time(Duration::from_secs(32))
184            .retry_limit(4)
185            .timeout(Duration::from_secs(600))
186            .read_timeout(Duration::from_secs(10));
187
188        assert!(cfg.force_auth);
189        assert_eq!(cfg.retry_limit, Some(4));
190        assert_eq!(cfg.max_retry_time, Some(Duration::from_secs(32)));
191        assert_eq!(cfg.timeout, Some(Duration::from_secs(600)));
192        assert_eq!(cfg.read_timeout, Some(Duration::from_secs(10)));
193    }
194
195    #[test]
196    fn testing_retry_settings() {
197        let mut cfg = RequestConfig::new();
198        assert_eq!(cfg.retry_limit, None);
199        cfg = cfg.retry_limit(10);
200        assert_eq!(cfg.retry_limit, Some(10));
201        cfg = cfg.disable_retry();
202        assert_eq!(cfg.retry_limit, Some(0));
203
204        let cfg = RequestConfig::short_retry();
205        assert_eq!(cfg.retry_limit, Some(3));
206    }
207}