Skip to main content

qubit_http/sse/
sse_reconnect_options.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Options and defaults for SSE reconnect with backoff.
11//!
12
13use std::time::Duration;
14
15use crate::{RetryDelay, RetryJitter, RetryOptions};
16
17/// Default upper bound for SSE reconnect delay backoff.
18pub(crate) const DEFAULT_SSE_MAX_RECONNECT_DELAY: Duration = Duration::from_secs(30);
19
20/// Default exponential backoff multiplier for SSE reconnect delay growth.
21pub(crate) const DEFAULT_SSE_RECONNECT_BACKOFF_MULTIPLIER: f64 = 2.0;
22
23/// Default maximum reconnect attempts after the initial stream open.
24pub(crate) const DEFAULT_SSE_MAX_RECONNECTS: u32 = 3;
25
26/// Builds default retry options for SSE reconnect.
27///
28/// # Returns
29/// Retry options matching SSE reconnect defaults.
30fn default_sse_retry_options() -> RetryOptions {
31    RetryOptions::new(
32        DEFAULT_SSE_MAX_RECONNECTS + 1,
33        None,
34        None,
35        RetryDelay::exponential(
36            Duration::from_secs(1),
37            DEFAULT_SSE_MAX_RECONNECT_DELAY,
38            DEFAULT_SSE_RECONNECT_BACKOFF_MULTIPLIER,
39        ),
40        RetryJitter::None,
41    )
42    .expect("SSE default retry options must be valid")
43}
44
45/// Reconnect behavior options for [`crate::HttpClient::execute_sse_with_reconnect`].
46#[derive(Debug, Clone, PartialEq)]
47pub struct SseReconnectOptions {
48    /// Retry options used by SSE reconnect delay calculation.
49    ///
50    /// `max_attempts` includes the initial stream-open attempt, so if callers
51    /// want at most `N` reconnects they should pass `max_attempts = N + 1`.
52    pub retry: RetryOptions,
53    /// Whether to reconnect when the SSE stream ends without an explicit error.
54    pub reconnect_on_eof: bool,
55    /// Whether to honor SSE `retry:` field as the next reconnect delay.
56    pub honor_server_retry: bool,
57    /// Optional upper bound applied to SSE `retry:` delay values from server
58    /// events.
59    ///
60    /// When `None`, reconnect runner derives a cap from retry delay strategy
61    /// when it has explicit max (`Random` / `Exponential`), otherwise it uses
62    /// internal default bound.
63    pub server_retry_max_delay: Option<Duration>,
64    /// Whether jitter should be applied when the reconnect delay comes from SSE
65    /// `retry:` field.
66    pub apply_jitter_to_server_retry: bool,
67}
68
69impl Default for SseReconnectOptions {
70    /// Builds default SSE reconnect options.
71    ///
72    /// # Returns
73    /// Default reconnect options with bounded reconnect attempts.
74    fn default() -> Self {
75        Self {
76            retry: default_sse_retry_options(),
77            reconnect_on_eof: true,
78            honor_server_retry: true,
79            server_retry_max_delay: None,
80            apply_jitter_to_server_retry: true,
81        }
82    }
83}
84
85impl SseReconnectOptions {
86    /// Creates default SSE reconnect options.
87    ///
88    /// # Returns
89    /// Same as [`SseReconnectOptions::default`].
90    pub fn new() -> Self {
91        Self::default()
92    }
93}