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}