sync15/
error.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use interrupt_support::Interrupted;
6
7// reexport logging helpers.
8#[allow(unused_imports)] // some only used with certain features.
9pub use error_support::{debug, error, info, trace, warn};
10
11/// This enum is to discriminate `StorageHttpError`, and not used as an error.
12#[cfg(feature = "sync-client")]
13#[derive(Debug, Clone)]
14pub enum ErrorResponse {
15    NotFound { route: String },
16    // 401
17    Unauthorized { route: String },
18    // 412
19    PreconditionFailed { route: String },
20    // 5XX
21    ServerError { route: String, status: u16 }, // TODO: info for "retry-after" and backoff handling etc here.
22    // Other HTTP responses.
23    RequestFailed { route: String, status: u16 },
24}
25
26pub type Result<T> = std::result::Result<T, Error>;
27
28#[derive(Debug, thiserror::Error)]
29pub enum Error {
30    #[cfg(feature = "crypto")]
31    #[error("Key {0} had wrong length, got {1}, expected {2}")]
32    BadKeyLength(&'static str, usize, usize),
33
34    #[cfg(feature = "crypto")]
35    #[error("SHA256 HMAC Mismatch error")]
36    HmacMismatch,
37
38    #[cfg(feature = "crypto")]
39    #[error("Crypto/NSS error: {0}")]
40    CryptoError(#[from] rc_crypto::Error),
41
42    #[cfg(feature = "crypto")]
43    #[error("Base64 decode error: {0}")]
44    Base64Decode(#[from] base64::DecodeError),
45
46    #[error("JSON error: {0}")]
47    JsonError(#[from] serde_json::Error),
48
49    #[error("Bad cleartext UTF8: {0}")]
50    BadCleartextUtf8(#[from] std::string::FromUtf8Error),
51
52    #[cfg(feature = "crypto")]
53    #[error("HAWK error: {0}")]
54    HawkError(#[from] rc_crypto::hawk::Error),
55
56    //
57    // Errors specific to this module.
58    //
59    #[cfg(feature = "sync-client")]
60    #[error("HTTP status {0} when requesting a token from the tokenserver")]
61    TokenserverHttpError(u16),
62
63    #[cfg(feature = "sync-client")]
64    #[error("HTTP storage error: {0:?}")]
65    StorageHttpError(ErrorResponse),
66
67    #[cfg(feature = "sync-client")]
68    #[error("Server requested backoff. Retry after {0:?}")]
69    BackoffError(std::time::SystemTime),
70
71    #[cfg(feature = "sync-client")]
72    #[error("Outgoing record is too large to upload")]
73    RecordTooLargeError,
74
75    // Do we want to record the concrete problems?
76    #[cfg(feature = "sync-client")]
77    #[error("Not all records were successfully uploaded")]
78    RecordUploadFailed,
79
80    /// Used for things like a node reassignment or an unexpected syncId
81    /// implying the app needs to "reset" its understanding of remote storage.
82    #[cfg(feature = "sync-client")]
83    #[error("The server has reset the storage for this account")]
84    StorageResetError,
85
86    #[cfg(feature = "sync-client")]
87    #[error("Unacceptable URL: {0}")]
88    UnacceptableUrl(String),
89
90    #[cfg(feature = "sync-client")]
91    #[error("Missing server timestamp header in request")]
92    MissingServerTimestamp,
93
94    #[cfg(feature = "sync-client")]
95    #[error("Unexpected server behavior during batch upload: {0}")]
96    ServerBatchProblem(&'static str),
97
98    #[cfg(feature = "sync-client")]
99    #[error("It appears some other client is also trying to setup storage; try again later")]
100    SetupRace,
101
102    #[cfg(feature = "sync-client")]
103    #[error("Client upgrade required; server storage version too new")]
104    ClientUpgradeRequired,
105
106    // This means that our global state machine needs to enter a state (such as
107    // "FreshStartNeeded", but the allowed_states don't include that state.)
108    // It typically means we are trying to do a "fast" or "read-only" sync.
109    #[cfg(feature = "sync-client")]
110    #[error("Our storage needs setting up and we can't currently do it")]
111    SetupRequired,
112
113    #[cfg(feature = "sync-client")]
114    #[error("Store error: {0}")]
115    StoreError(#[from] anyhow::Error),
116
117    #[cfg(feature = "sync-client")]
118    #[error("Network error: {0}")]
119    RequestError(#[from] viaduct::Error),
120
121    #[cfg(feature = "sync-client")]
122    #[error("Unexpected HTTP status: {0}")]
123    UnexpectedStatus(#[from] viaduct::UnexpectedStatus),
124
125    #[cfg(feature = "sync-client")]
126    #[error("URL parse error: {0}")]
127    MalformedUrl(#[from] url::ParseError),
128
129    #[error("The operation was interrupted.")]
130    Interrupted(#[from] Interrupted),
131}
132
133#[cfg(feature = "sync-client")]
134impl Error {
135    pub(crate) fn get_backoff(&self) -> Option<std::time::SystemTime> {
136        if let Error::BackoffError(time) = self {
137            Some(*time)
138        } else {
139            None
140        }
141    }
142}