proton_sdk/config.rs
1//! Client configuration and defaults.
2
3use std::time::Duration;
4
5/// Default base URL for the Proton Drive API.
6pub const DEFAULT_BASE_URL: &str = "https://drive-api.proton.me/";
7
8/// Default redirect URI sent with token refresh requests.
9pub const DEFAULT_REFRESH_REDIRECT_URI: &str = "https://proton.me";
10
11/// Default per-request timeout, matching `ProtonApiDefaults.DefaultTimeoutSeconds`.
12/// Applies to the JSON API calls (link details, listings, revision creation) —
13/// small requests where 30s is generous and a stall should fail fast.
14pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
15
16/// Default timeout for a single block-storage transfer (`get_storage_blob` /
17/// `post_storage_blob`). A block is up to 4 MiB and rides a different host from
18/// the API; on a slow uplink that transfer legitimately takes far longer than a
19/// JSON call, so it gets its own, much larger budget rather than tripping the
20/// 30s API timeout spuriously.
21pub const DEFAULT_STORAGE_TIMEOUT: Duration = Duration::from_secs(300);
22
23/// Default number of automatic retries on a retryable failure (rate-limit,
24/// transient transport error). The original attempt is not counted, so the
25/// total request budget is `1 + max_retries`.
26pub const DEFAULT_MAX_RETRIES: u32 = 3;
27
28/// Default base delay for exponential backoff between retries.
29pub const DEFAULT_RETRY_BASE_DELAY: Duration = Duration::from_millis(500);
30
31/// Default upper bound for a single backoff sleep. A server-supplied
32/// `Retry-After` is honoured even if it exceeds this.
33pub const DEFAULT_RETRY_MAX_DELAY: Duration = Duration::from_secs(30);
34
35/// Controls automatic retries on retryable responses (HTTP 408/429/502/503/504)
36/// and transient transport errors (timeout, connect).
37///
38/// Mirrors the Polly-style retry pipeline in the C# SDK: a server-supplied
39/// `Retry-After` header wins; otherwise the delay is exponential backoff
40/// (`base_delay * 2^attempt`, capped at `max_delay`) with full jitter.
41#[derive(Debug, Clone)]
42pub struct RetryPolicy {
43 /// Maximum number of retries after the initial attempt. `0` disables retry.
44 pub max_retries: u32,
45 /// Base delay for exponential backoff.
46 pub base_delay: Duration,
47 /// Cap on a single computed backoff sleep.
48 pub max_delay: Duration,
49}
50
51impl Default for RetryPolicy {
52 fn default() -> Self {
53 Self {
54 max_retries: DEFAULT_MAX_RETRIES,
55 base_delay: DEFAULT_RETRY_BASE_DELAY,
56 max_delay: DEFAULT_RETRY_MAX_DELAY,
57 }
58 }
59}
60
61impl RetryPolicy {
62 /// A policy that performs no automatic retries.
63 pub fn disabled() -> Self {
64 Self {
65 max_retries: 0,
66 ..Self::default()
67 }
68 }
69}
70
71/// Content type used for the `Accept` header on API requests.
72pub const API_CONTENT_TYPE: &str = "application/vnd.protonmail.api+json";
73
74/// Configuration for a Proton client session.
75///
76/// `app_version` is mandatory and must honour the operational requirements in
77/// the SDK README (`external-drive-{name}@{semver}-{channel}`), sent as the
78/// `x-pm-appversion` header.
79#[derive(Debug, Clone)]
80pub struct ProtonClientConfiguration {
81 pub base_url: String,
82 pub app_version: String,
83 pub user_agent: String,
84 pub refresh_redirect_uri: String,
85 pub request_timeout: Duration,
86 /// Timeout for a single block-storage transfer. Separate from
87 /// `request_timeout` so a large block on a slow link does not fail against
88 /// the (short) API timeout. See [`DEFAULT_STORAGE_TIMEOUT`].
89 pub storage_timeout: Duration,
90 pub retry_policy: RetryPolicy,
91}
92
93impl ProtonClientConfiguration {
94 /// Create a configuration with the given app version and otherwise defaults.
95 pub fn new(app_version: impl Into<String>) -> Self {
96 Self {
97 base_url: DEFAULT_BASE_URL.to_owned(),
98 app_version: app_version.into(),
99 user_agent: String::new(),
100 refresh_redirect_uri: DEFAULT_REFRESH_REDIRECT_URI.to_owned(),
101 request_timeout: DEFAULT_TIMEOUT,
102 storage_timeout: DEFAULT_STORAGE_TIMEOUT,
103 retry_policy: RetryPolicy::default(),
104 }
105 }
106
107 pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
108 self.base_url = base_url.into();
109 self
110 }
111
112 pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
113 self.user_agent = user_agent.into();
114 self
115 }
116
117 pub fn with_request_timeout(mut self, timeout: Duration) -> Self {
118 self.request_timeout = timeout;
119 self
120 }
121
122 pub fn with_storage_timeout(mut self, timeout: Duration) -> Self {
123 self.storage_timeout = timeout;
124 self
125 }
126
127 pub fn with_retry_policy(mut self, retry_policy: RetryPolicy) -> Self {
128 self.retry_policy = retry_policy;
129 self
130 }
131}