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