web_push/
error.rs

1use std::string::FromUtf8Error;
2use std::time::{Duration, SystemTime};
3use std::{convert::From, error::Error, fmt, io::Error as IoError};
4
5use base64::DecodeError;
6use http::uri::InvalidUri;
7use serde_json::error::Error as JsonError;
8
9#[derive(PartialEq, Debug, Clone, Ord, PartialOrd, Eq, Deserialize, Serialize, Hash)]
10pub enum WebPushError {
11    /// An unknown error happened encrypting the message,
12    Unspecified,
13    /// Please provide valid credentials to send the notification
14    Unauthorized,
15    /// Request was badly formed
16    BadRequest(Option<String>),
17    /// Contains an optional `Duration`, until the user can retry the request
18    ServerError(Option<Duration>),
19    /// The feature is not implemented yet
20    NotImplemented,
21    /// The provided URI is invalid
22    InvalidUri,
23    /// The URL specified is no longer valid and should no longer be used
24    EndpointNotValid,
25    /// The URL specified is invalid and should not be used again
26    EndpointNotFound,
27    /// Maximum allowed payload size is 3800 characters
28    PayloadTooLarge,
29    /// Could not initialize a TLS connection
30    TlsError,
31    /// Error in SSL signing
32    SslError,
33    /// Error in reading a file
34    IoError,
35    /// Make sure the message was addressed to a registration token whose
36    /// package name matches the value passed in the request (Google).
37    InvalidPackageName,
38    /// The TTL value provided was not valid or was not provided
39    InvalidTtl,
40    /// The Topic value provided was invalid
41    InvalidTopic,
42    /// The request was missing required crypto keys
43    MissingCryptoKeys,
44    /// One or more of the crypto key elements are invalid.
45    InvalidCryptoKeys,
46    /// Corrupted response data
47    InvalidResponse,
48    /// A claim had invalid data
49    InvalidClaims,
50    Other(String),
51}
52
53impl Error for WebPushError {}
54
55impl From<JsonError> for WebPushError {
56    fn from(_: JsonError) -> WebPushError {
57        WebPushError::InvalidResponse
58    }
59}
60
61impl From<FromUtf8Error> for WebPushError {
62    fn from(_: FromUtf8Error) -> WebPushError {
63        WebPushError::InvalidResponse
64    }
65}
66
67impl From<InvalidUri> for WebPushError {
68    fn from(_: InvalidUri) -> WebPushError {
69        WebPushError::InvalidUri
70    }
71}
72
73#[cfg(feature = "hyper-client")]
74impl From<hyper::Error> for WebPushError {
75    fn from(_: hyper::Error) -> Self {
76        Self::Unspecified
77    }
78}
79
80#[cfg(feature = "isahc-client")]
81impl From<isahc::Error> for WebPushError {
82    fn from(_: isahc::Error) -> Self {
83        Self::Unspecified
84    }
85}
86
87impl From<IoError> for WebPushError {
88    fn from(_: IoError) -> WebPushError {
89        WebPushError::IoError
90    }
91}
92
93impl From<DecodeError> for WebPushError {
94    fn from(_: DecodeError) -> WebPushError {
95        WebPushError::InvalidCryptoKeys
96    }
97}
98
99impl WebPushError {
100    pub fn short_description(&self) -> &'static str {
101        match *self {
102            WebPushError::Unspecified => "unspecified",
103            WebPushError::Unauthorized => "unauthorized",
104            WebPushError::BadRequest(_) => "bad_request",
105            WebPushError::ServerError(_) => "server_error",
106            WebPushError::NotImplemented => "not_implemented",
107            WebPushError::InvalidUri => "invalid_uri",
108            WebPushError::EndpointNotValid => "endpoint_not_valid",
109            WebPushError::EndpointNotFound => "endpoint_not_found",
110            WebPushError::PayloadTooLarge => "payload_too_large",
111            WebPushError::TlsError => "tls_error",
112            WebPushError::InvalidPackageName => "invalid_package_name",
113            WebPushError::InvalidTtl => "invalid_ttl",
114            WebPushError::InvalidTopic => "invalid_topic",
115            WebPushError::InvalidResponse => "invalid_response",
116            WebPushError::MissingCryptoKeys => "missing_crypto_keys",
117            WebPushError::InvalidCryptoKeys => "invalid_crypto_keys",
118            WebPushError::SslError => "ssl_error",
119            WebPushError::IoError => "io_error",
120            WebPushError::Other(_) => "other",
121            WebPushError::InvalidClaims => "invalidClaims",
122        }
123    }
124}
125
126impl fmt::Display for WebPushError {
127    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128        match *self {
129            WebPushError::Unspecified =>
130                write!(f, "An unknown error happened encrypting the message"),
131            WebPushError::Unauthorized =>
132                write!(f, "Please provide valid credentials to send the notification"),
133            WebPushError::BadRequest(_) =>
134                write!(f, "Request was badly formed"),
135            WebPushError::ServerError(_) =>
136                write!(f, "Server was unable to process the request, please try again later"),
137            WebPushError::PayloadTooLarge =>
138                write!(f, "Maximum allowed payload size is 3070 characters"),
139            WebPushError::InvalidUri =>
140                write!(f, "The provided URI is invalid"),
141            WebPushError::NotImplemented =>
142                write!(f, "The feature is not implemented yet"),
143            WebPushError::EndpointNotValid =>
144                write!(f, "The URL specified is no longer valid and should no longer be used"),
145            WebPushError::EndpointNotFound =>
146                write!(f, "The URL specified is invalid and should not be used again"),
147            WebPushError::TlsError =>
148                write!(f, "Could not initialize a TLS connection"),
149            WebPushError::SslError =>
150                write!(f, "Error signing with SSL"),
151            WebPushError::IoError =>
152                write!(f, "Error opening a file"),
153            WebPushError::InvalidPackageName =>
154                write!(f, "Make sure the message was addressed to a registration token whose package name matches the value passed in the request."),
155            WebPushError::InvalidTtl => write!(f, "The TTL value provided was not valid or was not provided"),
156            WebPushError::InvalidTopic => write!(f, "The Topic value provided was invalid"),
157            WebPushError::InvalidResponse => write!(f, "The response data couldn't be parses"),
158            WebPushError::MissingCryptoKeys  => write!(f, "The request is missing cryptographic keys"),
159            WebPushError::InvalidCryptoKeys  => write!(f, "The request is having invalid cryptographic keys"),
160            WebPushError::Other(_) => write!(f, "An unknown error when connecting the notification service"),
161            WebPushError::InvalidClaims => write!(f, "At least one JWT claim was invalid.")
162        }
163    }
164}
165
166pub struct RetryAfter;
167impl RetryAfter {
168    pub fn from_str(header_value: &str) -> Option<Duration> {
169        if let Ok(seconds) = header_value.parse::<u64>() {
170            Some(Duration::from_secs(seconds))
171        } else {
172            chrono::DateTime::parse_from_rfc2822(header_value)
173                .map(|date_time| {
174                    let systime: SystemTime = date_time.into();
175
176                    systime
177                        .duration_since(SystemTime::now())
178                        .unwrap_or_else(|_| Duration::new(0, 0))
179                })
180                .ok()
181        }
182    }
183}