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.
1415use std::{
16 fmt::{self, Debug},
17 num::NonZeroUsize,
18 time::Duration,
19};
2021use matrix_sdk_common::debug::DebugStructExt;
22use ruma::api::MatrixVersion;
2324use crate::http_client::DEFAULT_REQUEST_TIMEOUT;
2526/// Configuration for requests the `Client` makes.
27///
28/// This sets how often and for how long a request should be repeated. As well
29/// as how long a successful request is allowed to take.
30///
31/// By default requests are retried indefinitely and use no timeout.
32///
33/// # Examples
34///
35/// ```
36/// use matrix_sdk::config::RequestConfig;
37/// use std::time::Duration;
38///
39/// // This sets makes requests fail after a single send request and sets the timeout to 30s
40/// let request_config = RequestConfig::new()
41/// .disable_retry()
42/// .timeout(Duration::from_secs(30));
43/// ```
44#[derive(Copy, Clone)]
45pub struct RequestConfig {
46pub(crate) timeout: Duration,
47pub(crate) retry_limit: Option<u64>,
48pub(crate) retry_timeout: Option<Duration>,
49pub(crate) max_concurrent_requests: Option<NonZeroUsize>,
50pub(crate) force_auth: bool,
51pub(crate) force_matrix_version: Option<MatrixVersion>,
52}
5354#[cfg(not(tarpaulin_include))]
55impl Debug for RequestConfig {
56fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
57let Self {
58 timeout,
59 retry_limit,
60 retry_timeout,
61 force_auth,
62 max_concurrent_requests,
63 force_matrix_version,
64 } = self;
6566let mut res = fmt.debug_struct("RequestConfig");
67 res.field("timeout", timeout)
68 .maybe_field("retry_limit", retry_limit)
69 .maybe_field("retry_timeout", retry_timeout)
70 .maybe_field("max_concurrent_requests", max_concurrent_requests)
71 .maybe_field("force_matrix_version", force_matrix_version);
7273if *force_auth {
74 res.field("force_auth", &true);
75 }
7677 res.finish()
78 }
79}
8081impl Default for RequestConfig {
82fn default() -> Self {
83Self {
84 timeout: DEFAULT_REQUEST_TIMEOUT,
85 retry_limit: Default::default(),
86 retry_timeout: Default::default(),
87 max_concurrent_requests: Default::default(),
88 force_auth: false,
89 force_matrix_version: Default::default(),
90 }
91 }
92}
9394impl RequestConfig {
95/// Create a new default `RequestConfig`.
96#[must_use]
97pub fn new() -> Self {
98 Default::default()
99 }
100101/// Create a new `RequestConfig` with default values, except the retry limit
102 /// which is set to 3.
103#[must_use]
104pub fn short_retry() -> Self {
105Self::default().retry_limit(3)
106 }
107108/// This is a convince method to disable the retries of a request. Setting
109 /// the `retry_limit` to `0` has the same effect.
110#[must_use]
111pub fn disable_retry(mut self) -> Self {
112self.retry_limit = Some(0);
113self
114}
115116/// The number of times a request should be retried. The default is no
117 /// limit.
118#[must_use]
119pub fn retry_limit(mut self, retry_limit: u64) -> Self {
120self.retry_limit = Some(retry_limit);
121self
122}
123124/// The total limit of request that are pending or run concurrently.
125 /// Any additional request beyond that number will be waiting until another
126 /// concurrent requests finished. Requests are queued fairly.
127#[must_use]
128pub fn max_concurrent_requests(mut self, limit: Option<NonZeroUsize>) -> Self {
129self.max_concurrent_requests = limit;
130self
131}
132133/// Set the timeout duration for all HTTP requests.
134#[must_use]
135pub fn timeout(mut self, timeout: Duration) -> Self {
136self.timeout = timeout;
137self
138}
139140/// Set a timeout for how long a request should be retried. The default is
141 /// no timeout, meaning requests are retried forever.
142#[must_use]
143pub fn retry_timeout(mut self, retry_timeout: Duration) -> Self {
144self.retry_timeout = Some(retry_timeout);
145self
146}
147148/// Force sending authorization even if the endpoint does not require it.
149 /// Default is only sending authorization if it is required.
150#[must_use]
151pub fn force_auth(mut self) -> Self {
152self.force_auth = true;
153self
154}
155156/// Force the Matrix version used to select which version of the endpoint to
157 /// use.
158 ///
159 /// Can be used to force the use of a stable endpoint when the versions
160 /// advertised by the homeserver do not support it.
161#[must_use]
162pub(crate) fn force_matrix_version(mut self, version: MatrixVersion) -> Self {
163self.force_matrix_version = Some(version);
164self
165}
166}
167168#[cfg(test)]
169mod tests {
170use std::time::Duration;
171172use super::RequestConfig;
173174#[test]
175fn smoketest() {
176let cfg = RequestConfig::new()
177 .force_auth()
178 .retry_timeout(Duration::from_secs(32))
179 .retry_limit(4)
180 .timeout(Duration::from_secs(600));
181182assert!(cfg.force_auth);
183assert_eq!(cfg.retry_limit, Some(4));
184assert_eq!(cfg.retry_timeout, Some(Duration::from_secs(32)));
185assert_eq!(cfg.timeout, Duration::from_secs(600));
186 }
187188#[test]
189fn testing_retry_settings() {
190let mut cfg = RequestConfig::new();
191assert_eq!(cfg.retry_limit, None);
192 cfg = cfg.retry_limit(10);
193assert_eq!(cfg.retry_limit, Some(10));
194 cfg = cfg.disable_retry();
195assert_eq!(cfg.retry_limit, Some(0));
196197let cfg = RequestConfig::short_retry();
198assert_eq!(cfg.retry_limit, Some(3));
199 }
200}