reqwest_eventsource/
retry.rs

1//! Helpers to handle connection delays when receiving errors
2
3use crate::error::Error;
4use std::time::Duration;
5
6#[cfg(doc)]
7use crate::event_source::{Event, EventSource};
8
9/// Describes how an [`EventSource`] should retry on receiving an [`enum@Error`]
10pub trait RetryPolicy {
11    /// Submit a new retry delay based on the [`enum@Error`], last retry number and duration, if
12    /// available. A policy may also return `None` if it does not want to retry
13    fn retry(&self, error: &Error, last_retry: Option<(usize, Duration)>) -> Option<Duration>;
14
15    /// Set a new reconnection time if received from an [`Event`]
16    fn set_reconnection_time(&mut self, duration: Duration);
17}
18
19/// A [`RetryPolicy`] which backs off exponentially
20#[derive(Debug, Clone)]
21pub struct ExponentialBackoff {
22    /// The start of the backoff
23    pub start: Duration,
24    /// The factor of which to backoff by
25    pub factor: f64,
26    /// The maximum duration to delay
27    pub max_duration: Option<Duration>,
28    /// The maximum number of retries before giving up
29    pub max_retries: Option<usize>,
30}
31
32impl ExponentialBackoff {
33    /// Create a new exponential backoff retry policy
34    pub const fn new(
35        start: Duration,
36        factor: f64,
37        max_duration: Option<Duration>,
38        max_retries: Option<usize>,
39    ) -> Self {
40        Self {
41            start,
42            factor,
43            max_duration,
44            max_retries,
45        }
46    }
47}
48
49impl RetryPolicy for ExponentialBackoff {
50    fn retry(&self, _error: &Error, last_retry: Option<(usize, Duration)>) -> Option<Duration> {
51        if let Some((retry_num, last_duration)) = last_retry {
52            if self.max_retries.is_none() || retry_num < self.max_retries.unwrap() {
53                let duration = last_duration.mul_f64(self.factor);
54                if let Some(max_duration) = self.max_duration {
55                    Some(duration.min(max_duration))
56                } else {
57                    Some(duration)
58                }
59            } else {
60                None
61            }
62        } else {
63            Some(self.start)
64        }
65    }
66    fn set_reconnection_time(&mut self, duration: Duration) {
67        self.start = duration;
68        if let Some(max_duration) = self.max_duration {
69            self.max_duration = Some(max_duration.max(duration))
70        }
71    }
72}
73
74/// A [`RetryPolicy`] which always emits the same delay
75#[derive(Debug, Clone)]
76pub struct Constant {
77    /// The delay to return
78    pub delay: Duration,
79    /// The maximum number of retries to return before giving up
80    pub max_retries: Option<usize>,
81}
82
83impl Constant {
84    /// Create a new constant retry policy
85    pub const fn new(delay: Duration, max_retries: Option<usize>) -> Self {
86        Self { delay, max_retries }
87    }
88}
89
90impl RetryPolicy for Constant {
91    fn retry(&self, _error: &Error, last_retry: Option<(usize, Duration)>) -> Option<Duration> {
92        if let Some((retry_num, _)) = last_retry {
93            if self.max_retries.is_none() || retry_num < self.max_retries.unwrap() {
94                Some(self.delay)
95            } else {
96                None
97            }
98        } else {
99            Some(self.delay)
100        }
101    }
102    fn set_reconnection_time(&mut self, duration: Duration) {
103        self.delay = duration;
104    }
105}
106
107/// A [`RetryPolicy`] which never retries
108#[derive(Debug, Clone, Copy, Default)]
109pub struct Never;
110
111impl RetryPolicy for Never {
112    fn retry(&self, _error: &Error, _last_retry: Option<(usize, Duration)>) -> Option<Duration> {
113        None
114    }
115    fn set_reconnection_time(&mut self, _duration: Duration) {}
116}
117
118/// The default [`RetryPolicy`] when initializing an [`EventSource`]
119pub const DEFAULT_RETRY: ExponentialBackoff = ExponentialBackoff::new(
120    Duration::from_millis(300),
121    2.,
122    Some(Duration::from_secs(5)),
123    None,
124);