Skip to main content

apns_h2/
response.rs

1//! The APNs response types
2
3use std::fmt;
4
5/// The response data from APNs.
6#[derive(Debug)]
7pub struct Response {
8    /// If the notification was not successful, has the body content from APNs.
9    pub error: Option<ErrorBody>,
10
11    /// Is the value defined in the `NotificationOptions` or a new Uuid
12    /// generated by APNs.
13    pub apns_id: Option<String>,
14
15    /// A unique identifier that is only available in the Development environment.
16    /// Use this to query Delivery Log information for the corresponding notification.
17    pub apns_unique_id: Option<String>,
18
19    /// The HTTP response code.
20    ///
21    /// * 200 Success
22    /// * 400 Bad request
23    /// * 403 There was an error with the certificate or with the provider authentication token
24    /// * 405 The request used a bad `:method` value. Only `POST` requests are supported.
25    /// * 410 The device token is no longer active for the topic.
26    /// * 413 The notification payload was too large.
27    /// * 429 The server received too many requests for the same device token.
28    /// * 500 Internal server error.
29    /// * 503 The server is shutting down and unavailable.
30    pub code: u16,
31}
32
33/// The response body from APNs. Only available for errors.
34#[derive(Deserialize, Debug, PartialEq, Eq)]
35pub struct ErrorBody {
36    /// The error indicating the reason for the failure.
37    pub reason: ErrorReason,
38
39    /// If the value of the `ErrorReason` is `Unregistered`, the value of this
40    /// key is the last time at which APNs confirmed that the device token was
41    /// no longer valid for the topic.
42    ///
43    /// Stop pushing notifications until the device registers a token with a
44    /// later timestamp with your provider.
45    pub timestamp: Option<u64>,
46}
47
48/// A description what went wrong with the push notification.
49#[derive(Deserialize, Debug, PartialEq, Eq)]
50pub enum ErrorReason {
51    /// The collapse identifier exceeds the maximum allowed size.
52    BadCollapseId,
53
54    /// The specified device token is invalid. Verify that the request contains a valid token and that the token matches the environment.
55    BadDeviceToken,
56
57    /// The apns-expiration value is invalid.
58    BadExpirationDate,
59
60    /// The apns-id value is invalid.
61    BadMessageId,
62
63    /// The apns-priority value is invalid.
64    BadPriority,
65
66    /// The apns-topic value is invalid.
67    BadTopic,
68
69    /// The device token doesn’t match the specified topic.
70    DeviceTokenNotForTopic,
71
72    /// One or more headers are repeated.
73    DuplicateHeaders,
74
75    /// Idle timeout.
76    IdleTimeout,
77
78    /// The apns-push-type value is invalid.
79    InvalidPushType,
80
81    /// The device token isn’t specified in the request :path. Verify that the :path header contains the device token.
82    MissingDeviceToken,
83
84    /// The apns-topic header of the request isn’t specified and is required. The apns-topic header is mandatory when the client is connected using a certificate that supports multiple topics.
85    MissingTopic,
86
87    /// The message payload is empty.
88    PayloadEmpty,
89
90    /// Pushing to this topic is not allowed.
91    TopicDisallowed,
92
93    /// The certificate is invalid.
94    BadCertificate,
95
96    /// The client certificate doesn’t match the environment.
97    BadCertificateEnvironment,
98
99    /// The provider token is stale and a new token should be generated.
100    ExpiredProviderToken,
101
102    /// The specified action is not allowed.
103    Forbidden,
104
105    /// The provider token is not valid, or the token signature can’t be verified.
106    InvalidProviderToken,
107
108    /// No provider certificate was used to connect to APNs, and the authorization header is missing or no provider token is specified.
109    MissingProviderToken,
110
111    /// The key ID in the provider token isn’t related to the key ID of the token used in the first push of this connection. To use this token, open a new connection.
112    UnrelatedKeyIdInToken,
113
114    /// The key ID in the provider token doesn’t match the environment.
115    BadEnvironmentKeyIdInToken,
116
117    /// The request contained an invalid :path value.
118    BadPath,
119
120    /// The specified :method value isn’t POST.
121    MethodNotAllowed,
122
123    /// The device token has expired.
124    ExpiredToken,
125
126    /// The device token is inactive for the specified topic. There is no need to send further pushes to the same device token, unless your application retrieves the same device token, refer to Registering your app with APNs
127    Unregistered,
128
129    /// The message payload is too large. For information about the allowed payload size, refer to Create a POST request to APNs in Sending notification requests to APNs.
130    PayloadTooLarge,
131
132    /// The provider’s authentication token is being updated too often. Update the authentication token no more than once every 20 minutes.
133    TooManyProviderTokenUpdates,
134
135    /// Too many requests were made consecutively to the same device token.
136    TooManyRequests,
137
138    /// An internal server error occurred.
139    InternalServerError,
140
141    /// The service is unavailable.
142    ServiceUnavailable,
143
144    /// The APNs server is shutting down.
145    Shutdown,
146}
147
148impl fmt::Display for ErrorReason {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        let s = match *self {
151            ErrorReason::BadCollapseId => "The collapse identifier exceeds the maximum allowed size.",
152            ErrorReason::BadDeviceToken => {
153                "The specified device token was bad. Verify that the request contains a valid token and that the token matches the environment."
154            }
155            ErrorReason::BadExpirationDate => "The `apns_expiration` in `NotificationOptions` is bad.",
156            ErrorReason::BadMessageId => "The `apns_id` in `NotificationOptions` is bad.",
157            ErrorReason::BadPriority => "The `apns_priority` in `NotificationOptions` is bad.",
158            ErrorReason::BadTopic => "The `apns_topic` in `NotificationOptions` is bad.",
159            ErrorReason::DeviceTokenNotForTopic => "The device token does not match the specified topic.",
160            ErrorReason::DuplicateHeaders => "One or more headers were repeated.",
161            ErrorReason::IdleTimeout => "Idle time out.",
162            ErrorReason::InvalidPushType => "The apns-push-type value is invalid.",
163            ErrorReason::MissingDeviceToken => "The device token is not specified in the payload.",
164            ErrorReason::MissingTopic => {
165                "The `apns_topic` of the `NotificationOptions` was not specified and was required. The `apns_topic` header is mandatory when the client is connected using the `CertificateConnector` and the included PKCS12 file includes multiple topics, or when using the `TokenConnector`."
166            }
167            ErrorReason::PayloadEmpty => "The message payload was empty.",
168            ErrorReason::TopicDisallowed => "Pushing to this topic is not allowed.",
169            ErrorReason::BadCertificate => "The certificate was bad.",
170            ErrorReason::BadCertificateEnvironment => "The client certificate was for the wrong environment.",
171            ErrorReason::ExpiredProviderToken => {
172                "The provider token is stale and a new token should be generated."
173            }
174            ErrorReason::Forbidden => "The specified action is not allowed.",
175            ErrorReason::InvalidProviderToken => {
176                "The provider token is not valid or the token signature could not be verified."
177            }
178            ErrorReason::MissingProviderToken => {
179                "No provider certificate was used to connect to APNs and Authorization header was missing or no provider token was specified."
180            }
181            ErrorReason::UnrelatedKeyIdInToken => {
182                "The key ID in the provider token isn't related to the key ID of the token used in the first push of this connection."
183            }
184            ErrorReason::BadEnvironmentKeyIdInToken => {
185                "The key ID in the provider token doesn't match the environment."
186            }
187            ErrorReason::BadPath => "The request path value is bad.",
188            ErrorReason::MethodNotAllowed => "The request method was not `POST`.",
189            ErrorReason::ExpiredToken => "The device token has expired.",
190            ErrorReason::Unregistered => {
191                "The device token is inactive for the specified topic. You should stop sending notifications to this token."
192            }
193            ErrorReason::PayloadTooLarge => "The message payload was too large (4096 bytes)",
194            ErrorReason::TooManyProviderTokenUpdates => "The provider token is being updated too often.",
195            ErrorReason::TooManyRequests => {
196                "Too many requests were made consecutively to the same device token."
197            }
198            ErrorReason::InternalServerError => "An internal server error occurred.",
199            ErrorReason::ServiceUnavailable => "The service is unavailable.",
200            ErrorReason::Shutdown => "The server is shutting down.",
201        };
202
203        f.write_str(s)
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210    use serde_json;
211
212    #[test]
213    fn test_error_response_parsing() {
214        let errors = vec![
215            (ErrorReason::BadCollapseId, "BadCollapseId", None),
216            (ErrorReason::BadDeviceToken, "BadDeviceToken", None),
217            (ErrorReason::BadExpirationDate, "BadExpirationDate", None),
218            (ErrorReason::BadMessageId, "BadMessageId", None),
219            (ErrorReason::BadPriority, "BadPriority", None),
220            (ErrorReason::BadTopic, "BadTopic", None),
221            (
222                ErrorReason::DeviceTokenNotForTopic,
223                "DeviceTokenNotForTopic",
224                None,
225            ),
226            (ErrorReason::DuplicateHeaders, "DuplicateHeaders", None),
227            (ErrorReason::IdleTimeout, "IdleTimeout", None),
228            (ErrorReason::InvalidPushType, "InvalidPushType", None),
229            (ErrorReason::MissingDeviceToken, "MissingDeviceToken", None),
230            (ErrorReason::MissingTopic, "MissingTopic", None),
231            (ErrorReason::PayloadEmpty, "PayloadEmpty", None),
232            (ErrorReason::TopicDisallowed, "TopicDisallowed", None),
233            (ErrorReason::BadCertificate, "BadCertificate", None),
234            (
235                ErrorReason::BadCertificateEnvironment,
236                "BadCertificateEnvironment",
237                None,
238            ),
239            (ErrorReason::ExpiredProviderToken, "ExpiredProviderToken", None),
240            (ErrorReason::Forbidden, "Forbidden", None),
241            (ErrorReason::InvalidProviderToken, "InvalidProviderToken", None),
242            (ErrorReason::MissingProviderToken, "MissingProviderToken", None),
243            (ErrorReason::UnrelatedKeyIdInToken, "UnrelatedKeyIdInToken", None),
244            (
245                ErrorReason::BadEnvironmentKeyIdInToken,
246                "BadEnvironmentKeyIdInToken",
247                None,
248            ),
249            (ErrorReason::BadPath, "BadPath", None),
250            (ErrorReason::MethodNotAllowed, "MethodNotAllowed", None),
251            (ErrorReason::ExpiredToken, "ExpiredToken", None),
252            (ErrorReason::Unregistered, "Unregistered", Some(1508249865488u64)),
253            (ErrorReason::PayloadTooLarge, "PayloadTooLarge", None),
254            (
255                ErrorReason::TooManyProviderTokenUpdates,
256                "TooManyProviderTokenUpdates",
257                None,
258            ),
259            (ErrorReason::TooManyRequests, "TooManyRequests", None),
260            (ErrorReason::InternalServerError, "InternalServerError", None),
261            (ErrorReason::ServiceUnavailable, "ServiceUnavailable", None),
262            (ErrorReason::Shutdown, "Shutdown", None),
263        ];
264
265        for error in errors.into_iter() {
266            let response_data = match error.2 {
267                None => json!({"reason": error.1}),
268                Some(ts) => json!({"reason": error.1, "timestamp": ts}),
269            };
270
271            let response_string = serde_json::to_string(&response_data).unwrap();
272
273            let response_body: ErrorBody = serde_json::from_str(&response_string).unwrap();
274
275            let expected_body = match error.2 {
276                None => ErrorBody {
277                    reason: error.0,
278                    timestamp: None,
279                },
280                Some(ts) => ErrorBody {
281                    reason: error.0,
282                    timestamp: Some(ts),
283                },
284            };
285
286            assert_eq!(expected_body, response_body);
287        }
288    }
289}