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