From 3f61fac159a3fee3f19d0dd1e3b2a75500353c4a Mon Sep 17 00:00:00 2001 From: Sergey Petrov Date: Tue, 9 Feb 2016 16:45:43 +0300 Subject: [PATCH] Add error handling for sending notifications --- apns2/client.py | 10 ++- apns2/errors.py | 157 +++++++++++++++++++++++++++++++++++++++++++++++ apns2/errors.pyi | 1 + 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 apns2/errors.py create mode 100644 apns2/errors.pyi diff --git a/apns2/client.py b/apns2/client.py index 6501d98..fbd99da 100644 --- a/apns2/client.py +++ b/apns2/client.py @@ -1,9 +1,12 @@ from enum import Enum from json import dumps +import json from hyper import HTTP20Connection from hyper.tls import init_context +from apns2.errors import exception_class_for_reason + class NotificationPriority(Enum): Immediate = 10 @@ -26,4 +29,9 @@ class APNsClient(object): headers['apns-topic'] = topic url = '/3/device/{}'.format(token_hex) - self.__connection.request('POST', url, json_payload, headers) + stream_id = self.__connection.request('POST', url, json_payload, headers) + resp = self.__connection.get_response(stream_id) + if resp.status != 200: + raw_data = resp.read().decode('utf-8') + data = json.loads(raw_data) + raise exception_class_for_reason(data['reason']) diff --git a/apns2/errors.py b/apns2/errors.py new file mode 100644 index 0000000..0cd6ec8 --- /dev/null +++ b/apns2/errors.py @@ -0,0 +1,157 @@ +class APNsException(Exception): + pass + + +class InternalException(APNsException): + """This exception should not thrown. If it is, please report this bug""" + pass + + +class BadPayloadException(APNsException): + """Something bad with payload""" + pass + + +class PayloadEmpty(BadPayloadException): + """The message payload was empty.""" + pass + + +class PayloadTooLarge(BadPayloadException): + """The message payload was too large. The maximum payload size is 4096 bytes.""" + pass + + +class BadTopic(BadPayloadException): + """The apns-topic was invalid.""" + pass + + +class TopicDisallowed(BadPayloadException): + """Pushing to this topic is not allowed.""" + pass + + +class BadMessageId(InternalException): + """The apns-id value is bad.""" + pass + + +class BadExpirationDate(BadPayloadException): + """The apns-expiration value is bad.""" + pass + + +class BadPriority(InternalException): + """The apns-priority value is bad.""" + pass + + +class MissingDeviceToken(APNsException): + """The device token is not specified in the request :path. + Verify that the :path header contains the device token.""" + pass + + +class BadDeviceToken(APNsException): + """The specified device token was bad. + Verify that the request contains a valid token and that the token matches the environment.""" + pass + + +class DeviceTokenNotForTopic(APNsException): + """The device token does not match the specified topic.""" + + +class Unregistered(APNsException): + """The device token is inactive for the specified topic.""" + + +class DuplicateHeaders(InternalException): + """One or more headers were repeated.""" + pass + + +class BadCertificateEnvironment(APNsException): + """The client certificate was for the wrong environment.""" + pass + + +class BadCertificate(APNsException): + """The certificate was bad.""" + pass + + +class Forbidden(APNsException): + """The specified action is not allowed.""" + pass + + +class BadPath(APNsException): + """The request contained a bad :path value.""" + pass + + +class MethodNotAllowed(InternalException): + """The specified :method was not POST.""" + pass + + +class TooManyRequests(APNsException): + """Too many requests were made consecutively to the same device token.""" + pass + + +class IdleTimeout(APNsException): + """Idle time out.""" + pass + + +class Shutdown(APNsException): + """The server is shutting down.""" + pass + + +class InternalServerError(APNsException): + """An internal server error occurred.""" + pass + + +class ServiceUnavailable(APNsException): + """The service is unavailable.""" + pass + + +class MissingTopic(BadPayloadException): + """The apns-topic header of the request was not specified and was required. + The apns-topic header is mandatory when the client is connected using a certificate + that supports multiple topics.""" + pass + + +def exception_class_for_reason(reason): + return { + 'PayloadEmpty': PayloadEmpty, + 'PayloadTooLarge': PayloadTooLarge, + 'BadTopic': BadTopic, + 'TopicDisallowed': TopicDisallowed, + 'BadMessageId': BadMessageId, + 'BadExpirationDate': BadExpirationDate, + 'BadPriority': BadPriority, + 'MissingDeviceToken': MissingDeviceToken, + 'BadDeviceToken': BadDeviceToken, + 'DeviceTokenNotForTopic': DeviceTokenNotForTopic, + 'Unregistered': Unregistered, + 'DuplicateHeaders': DuplicateHeaders, + 'BadCertificateEnvironment': BadCertificateEnvironment, + 'BadCertificate': BadCertificate, + 'Forbidden': Forbidden, + 'BadPath': BadPath, + 'MethodNotAllowed': MethodNotAllowed, + 'TooManyRequests': TooManyRequests, + 'IdleTimeout': IdleTimeout, + 'Shutdown': Shutdown, + 'InternalServerError': InternalServerError, + 'ServiceUnavailable': ServiceUnavailable, + 'MissingTopic': MissingTopic, + }[reason] diff --git a/apns2/errors.pyi b/apns2/errors.pyi new file mode 100644 index 0000000..83e08f6 --- /dev/null +++ b/apns2/errors.pyi @@ -0,0 +1 @@ +def exception_class_for_reason(reason: str) -> type: ...